Próbuję napisać test dla metody, która zgłasza wyjątek niestandardowy. Nie udaje się z powodu błędu asercji. Co można zrobić, aby poprawnie wyłapać wyjątek i przejść test?

Sposób obsługi:

@Service
public class CustomServiceImpl implements CustomService {
    @Autowired
    UserUtil userUtil;
    public ResultDTO getResultDto (String type, Long id) throws CustomException {
        User user = userUtil.getCurrentUser();
        if (user == null) {
            throw new CustomException("User does not exist");
        }
    }
}

Metoda badania:

@MockBean
CustomServiceImpl  customServiceImpl ;

@Test
public void test01_getResultDto() {
    UserUtil userUtil = Mockito.mock(UserUtil.class);
    Mockito.when(userUtil.getCurrentUser()).thenReturn(null);
    Assertions.assertThatThrownBy(() -> customServiceImpl.getResultDto (Mockito.anyString(), Mockito.anyLong())) 
        .isInstanceOf(CustomException .class)
        .hasMessage("User does not exist");
}

Ten test nie powiedzie się z następującym błędem:

java.lang.AssertionError: Expecting code to raise a throwable.
    at com.ps.service.CustomServiceImplTest.test01_getResultDto(CustomServiceImplTest.java:62)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:252)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:89)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:41)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:541)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:763)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:463)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:209)


--------------------------- EDYTOWAĆ ---------------------- - Zmieniono test kodu tak, aby zawierał następujące elementy

@Mock
UserUtil userUtil;
@InjectMocks
CustomServiceImpl cutomServiceImpl;

@Before
public void setUp() throws Exception {
    MockitoAnnotations.initMocks(this);
}

@Test
public void test01_getResultDto() {
    when(userUtil.getCurrentUser()).thenReturn(null);
    assertThatThrownBy(() -> customServiceImpl.getResultDto ("type", 1L)) 
        .isInstanceOf(CustomException .class)
        .hasMessage("User does not exist");
}

Wydaje się, że działa.

Dzięki radom w komentarzach.

2
Vishnu Soman 20 listopad 2019, 14:55
1
Wywołanie metody customServiceImpl.getResultDto() nie zgłasza wyjątku. To właśnie mówi AssertionError.
 – 
keuleJ
20 listopad 2019, 15:00
3
Testujesz makietę (@MockBean CustomServiceImpl). To nie ma sensu. To, co powinieneś kpić podczas testowania usługi A, to zależności A, tj. usługi używane i wstrzykiwane w A. Nie powinieneś potrzebować testu integracyjnego Spring, aby to przetestować. Tylko zwykły stary test jednostkowy. Zaczynasz od wstrzykiwania konstruktora zamiast wstrzykiwania pola. Tworzysz makieta UserUtil (zależność). Tworzysz prawdziwą instancję CustomServiceImpl za pomocą new CustomServiceImpl(mockUserUtil). Następnie wywołujesz swoją metodę. Ta metoda, BTW, nie powinna przyjmować żadnych argumentów, ponieważ ich nie używa.
 – 
JB Nizet
20 listopad 2019, 15:02
Jest w porządku. Musisz utworzyć nową instancję CustomServiceImpl i wstrzyknąć jego zależności (UserUtil) jako makiety. Ponadto nie używasz @MockBean do testów jednostkowych, ale zamiast tego @Mock. MockBean służy do testów integracyjnych.
 – 
Dherik
20 listopad 2019, 15:06
Czy powinienem napisać konstruktor w CustomServiceImpl, aby przyjąć argument UserUtil? Próbowałem umieścić mock wewnątrz metody testowej, więc: UserUtil userUtil = Mockito.mock(UserUtil.class); KpiServiceImpl kpiService = new KpiServiceImpl(userUtil); Ale część konstruktora jest pokazana jako błąd.
 – 
Vishnu Soman
20 listopad 2019, 15:31
Tak, powinieneś to zrobić. To sprawia, że ​​zależności są jawne i umożliwia łatwe wstrzykiwanie mocków, bez konieczności polegania na odbiciach i adnotacjach Mock.
 – 
JB Nizet
20 listopad 2019, 16:34

2 odpowiedzi

Dokonałem odpowiednich zmian w moim oryginalnym kodzie i następująca konfiguracja działa dobrze.

@Mock
UserUtil userUtil;
@InjectMocks
CustomServiceImpl cutomServiceImpl;

@Before
public void setUp() throws Exception {
    MockitoAnnotations.initMocks(this);
}

@Test
public void test01_getResultDto() {
    when(userUtil.getCurrentUser()).thenReturn(null);
    assertThatThrownBy(() -> customServiceImpl.getResultDto ("type", 1L)) 
        .isInstanceOf(CustomException .class)
        .hasMessage("User does not exist");
}
2
Vishnu Soman 21 listopad 2019, 16:38

Możesz to zrobić w inny sposób:

@MockBean
CustomServiceImpl  customServiceImpl ;
@Rule
public ExpectedException exceptionRule = ExpectedException.none();

@Test
public void test01_getResultDto() {
    exceptionRule.expect(CustomException.class);
    UserUtil userUtil = Mockito.mock(UserUtil.class);
    Mockito.when(userUtil.getCurrentUser()).thenReturn(null);
    customServiceImpl.getResultDto ("type", 1L);
}
0
Sanket Patel 21 listopad 2019, 13:12