тест mockito весной провалился при вызове метода mockito - PullRequest
1 голос
/ 21 марта 2019

вот две пружины Service;

public interface Service01 {
     String test01(String name);
}

@Service 
public class Service01Impl implements Service01 { 

     @Override
     public String test01(String name) {
          if (!"123".equals(name)) {
              throw new RuntimeException("name is illegal");
          }
          return name;
     }
}

@Service 
public class Service02 { 
     @Autowired
     private Service01 service01;
     @Autowired
     private Service03 service03;

     public String test02(String name) {
          service03.checkNameNotNull(name);
          return service01.test01(name);
     }
}

@Service 
public class Service03 { 
     public void checkNameNotNull(String name) {
          if (name == null) {
              throw new RuntimeException("name is null");
          }
     }
}

тогда вот мой testClass:

public class TestCalss {
    @Mock
    private Service01 service01;
    @Autowired
    private Service02 service02;

    @Test
    public void testMock() {
        // mock service
        when(service01.name("abc")).thenReturn("mock return abc");

        // here will be success
        String nameAbc = service01.test01("abc")
        Assert.assertEquals("mock return abc", nameAbc);

        // but here got exception: `name is illegal`
        String name = service02.test02("abc");
        Assert.assertEquals("mock return abc", name);
    }

}

Q1: почему получил исключение в строке: String name = service02.test02("abc");

java.lang.RuntimeException: name is illegal

Q2: как издеваться Service01, когда я звоню Service02?

- редактировать

Q2 исправлено с помощью @InjectMocks

Q3: как только издеваться Service01, но Service03 не издеваться.

Я хочу, чтобы этот чек был без макета service03.checkNameNotNull(name);

1 Ответ

1 голос
/ 21 марта 2019

Вам нужно использовать ArgumentMatcher при настройке макета - вместо передачи строкового литерала "abc", вам нужно, чтобы ваша строка выглядела как when(service01.name(eq("abc"))).thenReturn...

Если вы не возражаете против фактической строки, то есть и другие сопоставители, которые вы можете использовать, например anyString().

Вы также не хотите использовать @Autowired в своем тесте - если вы хотите, чтобы тестируемый класс автоматически вставлял в него mock, вам нужно создать экземпляры mock и сделать их инъекционными, а не реальными bean-компонентами Spring.

Самый простой способ сделать это - использовать аннотации MockitoJUnitRunner и @InjectMocks:

@RunWith(MockitoJUnitRunner.class)
public class TestCalss {
    @Mock
    private Service01 service01;
    @InjectMocks
    private Service02 service02;

...

Чтобы внедрить класс, который не является фиктивным, вам нужно использовать аннотацию @Before, с некоторым способом передачи объекта в класс (как в обычном Java-коде).

Мой предпочтительный способ сделать это - иметь конструктор, который устанавливает зависимости как частные конечные поля класса, но Spring также предоставляет класс ReflectionTestUtils, который можно использовать, если вы действительно хотите придерживаться закрытых полей без сеттеры.

Так что-то вроде этого:

@Service 
public class Service02 { 

     private final Service01 service01;
     private final Service03 service03;

     @Autowired
     public Service02 (Service01 service01, Service03 service03) {
         this.service01 = service01;
         this.service03 = service03;
     }

    ...
}

@RunWith(MockitoJUnitRunner.class)
public class TestClass {

    @Mock
    private Service01 service01;

    private Service02 underTest;

    @Before
    public void setup() {
        this.underTest = new Service02(this.service01, new Service03());
    }
    ...
}

Если вы хотите вызвать реальный метод для объекта, который иначе хотите смоделировать, то это может быть признаком того, что ваш класс делает слишком много и на самом деле должен быть двумя или более меньшими классами, но это также возможно: when(someMock.someMethod()).thenCallRealMethod();

...