Объект InjectMocks является нулевым в модульном тестировании - PullRequest
0 голосов
/ 01 марта 2020

Это мои первые джунтовые тесты с использованием Mockito. Я сталкиваюсь с проблемой NPE для сервиса, который использовался в @InjectMocks. Я смотрел на другие решения, но даже следуя им, он показывает то же самое. Вот мой код.

@RunWith(MockitoJUnitRunner.class)

public class CustomerStatementServiceTests {

    @InjectMocks
    private BBServiceImpl bbService;

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

    }

/**
 *  This test is to verify SUCCESS response
 */

@Test
public void testSuccess() { 

    BBResponse response = bbService.processDetails(txs);
    assertEquals("SUCCESSFUL" ,response.getResult());
}
}

BBServiceImpl

@Service
public class BBServiceImpl implements BBService {

final static Logger log = Logger.getLogger(BBServiceImpl.class);



public BBResponse process(List<Customer> customers) { 
  // My business logic goes here
}
}

Pom. xml

        <!-- https://mvnrepository.com/artifact/org.mockito/mockito-core -->
    <dependency>
        <groupId>org.mockito</groupId>
        <artifactId>mockito-core</artifactId>
        <version>2.23.4</version>
        <scope>test</scope>
    </dependency>




    <!-- https://mvnrepository.com/artifact/junit/junit -->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>

Мой объект "bbService" здесь равен нулю

Я что-то здесь упускаю?

Ответы [ 2 ]

1 голос
/ 01 марта 2020

После обсуждения, которое предоставило дополнительную информацию, ответ может быть обобщен как проблема конфигурации maven между junit4 и junit5.

java.lang.NullPointerException
at com.cts.rabo.CustomerStatementServiceTests.testSuccess(CustomerStatementServiceTests.java:83)
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.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:675)
at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60) 
...

Трассировка стека показала явное использование junit5 engine.

В pom также включена следующая зависимость:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <version>2.2.5.RELEASE</version>
    <scope>test</scope>
    <exclusions>
        <exclusion>
            <groupId>org.junit.vintage</groupId>
            <artifactId>junit-vintage-engine</artifactId>
        </exclusion>
    </exclusions>
</dependency>

До Spring Boot 2.2.0.RELEASE, тест spring-boot-starter-test транзитивно включал зависимость junit4. Spring Boot 2.2.0 и выше, вместо него включен Junit Jupiter.

И в соответствии с этим ответом исключение, по-видимому, препятствует выполнению.

Удаление исключения решил проблему для меня.


Я рекомендую переключиться на junit5, если нет очевидных требований для использования junit4.

Проверьте этот ответ для получения дополнительной информации о том, как использовать mockito вместе с junit5.

0 голосов
/ 01 марта 2020

Я думаю, что вы неправильно используете аннотацию @InjectMocks

Имеет смысл, если у вас есть реальный класс (обычно тот, который вы хотите проверить), который имеет зависимости , которые инициализируются с помощью Внедрение в конструктор или в сеттер. С другой стороны,

BBServiceImpl не имеет реальных зависимостей.

Теперь вы можете вручную создавать макеты для этих зависимостей, а затем создавать экземпляр тестируемого класса. или пусть mockito определит способ создания класса и «закачает» макеты для вас. Вы по-прежнему хотите создавать макеты с помощью аннотации @Mock, поскольку вы хотите указать ожидания во время теста.

Вот пример того, как это должно выглядеть:

public interface BBService {
    int process(String s); // simplified but still illustrates the concept
}

public class BBServiceImpl implements BBService {
    //here is a dependency that should be mocked when we will test BBServiceImpl class

    private  SizeCalculator sizeCalculator;

    // I use constructor injection here
    public BBServiceImpl(SizeCalculator sizeCalculator) {
        this.sizeCalculator = sizeCalculator;
    }

    public int process(String s) {
        // here is a dependency invocation
        return sizeCalculator.calculateSize(s);
    }
}

Как видите, реализация имеет реальную зависимость, которую можно смоделировать во время теста.

Теперь эта зависимость выглядит следующим образом:

public interface SizeCalculator {
    int calculateSize(String s);
}

public class SizeCalculatorImpl implements SizeCalculator {
    public int calculateSize(String s) {
        return s.length();
    }
}

Хорошо, теперь давайте создадим тест за BBServiceImpl. Мы не хотим создавать SizeCalculatorImpl и предпочитаем предоставлять макет для него и указывать ожидание для сгенерированного прокси во время выполнения, созданного из интерфейса SizeCalcluator:

@RunWith(MockitoJUnitRunner.class)
public class MockitoInjectMocksTest {

    // I need a reference on my mock because I'll specify expectations during the tests
    @Mock
    private SizeCalculator sizeCalculator;

    @InjectMocks
    private BBServiceImpl underTest;

    @Test
    public void testInjectMocks() {
        Mockito.when(sizeCalculator.calculateSize("hello")).thenReturn(5);

       int actual = underTest.process("hello");

        assertEquals(actual, 5);
    }
}

Обратите внимание на строку

@InjectMocks
private BBServiceImpl underTest;

Mockito найдет конструктор и введет макет

Конечно, в этом случае мы можем сделать это вручную:

@RunWith(MockitoJunitRunner.class)
public class SampleManualTest {
    @Mock
    private SizeCalculator sizeCalc;

    private BBServiceImpl underTest;
    @Before
    public void init() {
        this.underTest = new BBServiceImpl(sizeCalc);
    }
}

Но подумайте о случае, когда есть Есть много зависимостей в классе BBServiceImpl, InjectMocks может быть более удобным в этом случае +, если используется инъекция сеттера, этот способ может сохранять явные вызовы сеттерам.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...