Mockito - NullpointerException при застосуванні методу


80

Тож я почав писати тести для нашого проекту Java-Spring.

Я використовую JUnit та Mockito. Кажуть, що коли я використовую опцію when () ... thenReturn (), я можу знущатися над послугами, не імітуючи їх або близько того. Отже, що я хочу зробити, це встановити:

when(classIwantToTest.object.get().methodWhichReturnsAList(input))thenReturn(ListcreatedInsideTheTestClass)  

Але незалежно від того, яке пропозицію коли я роблю, я завжди отримую NullpointerException, що, звичайно, має сенс, оскільки введення є null.

Також коли я намагаюся знущатись із іншого методу з об’єкта:

when(object.method()).thenReturn(true)

Там я також отримую Nullpointer, оскільки метод потребує змінної, яка не встановлена.

Але я хочу використовувати, коли () .. thenReturn (), щоб обійти створення цієї змінної тощо. Я просто хочу переконатися, що якщо будь-який клас викликає цей метод, то, незважаючи ні на що, просто поверніть true або список вище.

Це в основному непорозуміння з мого боку, чи щось інше не так?

Код:

public class classIWantToTest implements classIWantToTestFacade{
        @Autowired
        private SomeService myService;

        @Override
        public Optional<OutputData> getInformations(final InputData inputData) {
            final Optional<OutputData> data = myService.getListWithData(inputData);
            if (data.isPresent()) {
                final List<ItemData> allData = data.get().getItemDatas();
                    //do something with the data and allData
                return data;
            }

            return Optional.absent();
        }   
}

І ось мій тестовий клас:

public class Test {

    private InputData inputdata;

    private ClassUnderTest classUnderTest;

    final List<ItemData> allData = new ArrayList<ItemData>();

    @Mock
    private DeliveryItemData item1;

    @Mock
    private DeliveryItemData item2;



    @Mock
    private SomeService myService;


    @Before
    public void setUp() throws Exception {
        classUnderTest = new ClassUnderTest();
        myService = mock(myService.class); 
        classUnderTest.setService(myService);
        item1 = mock(DeliveryItemData.class);
        item2 = mock(DeliveryItemData.class);

    }


    @Test
    public void test_sort() {
        createData();
        when(myService.getListWithData(inputdata).get().getItemDatas());

        when(item1.hasSomething()).thenReturn(true);
        when(item2.hasSomething()).thenReturn(false);

    }

    public void createData() {
        item1.setSomeValue("val");
        item2.setSomeOtherValue("test");

        item2.setSomeValue("val");
        item2.setSomeOtherValue("value");

        allData.add(item1);
        allData.add(item2);


}

3
об’єкт, коли в () повинен бути макет, створений Mockito.mock (), він не працює для створених вручну реальних об’єктів - не впевнений, що це ваша проблема, оскільки я насправді не потрапляю туди, де ваша проблема ...
Nadir

1
Будь ласка, опублікуйте код.
Michael Lloyd Lee mlk

1
Здається, classIwantToTest.object.get()результат не є фікцією або get()повертає нуль. Але, будь ласка, опублікуйте свій код, демонструючи свій тест та ініціалізацію своїх макетів.
vikingsteve

Додано трохи коду. Потрібно змінити імена змінних, оскільки це від моєї компанії;).
user5417542

посилайтеся на мою відповідь нижче
ананд криш

Відповіді:


62

Повернене значення за замовчуванням для методів, які ви ще не заглушили, - це falseлогічні методи, порожня колекція або карта для методів, що повертають колекції чи карти та nullінше.

Це також стосується викликів методів усередині when(...). У вашому прикладіwhen(myService.getListWithData(inputData).get()) викличе NullPointerException, тому що myService.getListWithData(inputData)він null- він раніше не заглушався.

Одним із варіантів є створення макетів для всіх проміжних повернутих значень та заглушення їх перед використанням. Наприклад:

ListWithData listWithData = mock(ListWithData.class);
when(listWithData.get()).thenReturn(item1);
when(myService.getListWithData()).thenReturn(listWithData);

Або ж ви можете вказати іншу відповідь за замовчуванням під час створення макета, щоб змусити методи повертати новий макет замість null: RETURNS_DEEP_STUBS

SomeService myService = mock(SomeService.class, Mockito.RETURNS_DEEP_STUBS);
when(myService.getListWithData().get()).thenReturn(item1);

Вам слід прочитати Javadoc з Mockito.RETURNS_DEEP_STUBS, який пояснює це більш докладно, а також містить деякі попередження щодо його використання.

Сподіваюся, це допоможе. Просто зауважте, що у вашому прикладі коду є більше проблем, таких як відсутність затвердження або перевірки тверджень та виклик установників на макети (що не має жодного ефекту).


Де я можу знайти специфікацію / документи для першого речення відповіді? Дякую!
Штурцль


176

У мене була ця проблема, і моя проблема полягала в тому, що я викликав свій метод any()замість anyInt(). Отже, я мав:

doAnswer(...).with(myMockObject).thisFuncTakesAnInt(any())

і мені довелося змінити його на:

doAnswer(...).with(myMockObject).thisFuncTakesAnInt(anyInt())

Я не уявляю, чому це спричинило NullPointerException. Можливо, це допоможе наступній бідній душі.


51
Це спрацювало для мене. Залишаючи цей коментар для наступної бідної душі.
Паван Кумар,

49
Збігаються не працюють для примітивів, тому, якщо аргумент примітивний, вам знадобиться anyChar / Int / Boolean тощо
micah

16
не дозволяти any()працювати для всіх змінних і не проявляти це як NPE рівносильно атаці кібервійни на команди розробників.
перевіряв

Причиною тому є те, що any () повертає загальне посилання на об'єкт. Цей об’єкт не упаковується в ціле число, тому загальним типом визначено „Ціле число”. Однак об’єкт є нульовим, тому розпаковування не отримує доступ до нульового покажчика. anyInt () надає власне ціле число, тому працює.
Едвард

змінивши any () на anyInt () вирішили це для мене теж - дякую! (мій випадок включав Java-тести та заняття kotlin)
Maggnetix

49

У мене була та сама проблема, і моя проблема полягала просто в тому, що я не анотував клас належним чином, використовуючи @RunWith. У вашому прикладі переконайтеся, що у вас є:

@RunWith(MockitoJUnitRunner.class)
public class Test {
...

Як тільки я це зробив, NullPointerExceptions зникли.


1
Я отримав нульовий покажчик bcoz @RunWith (PowerMockRunner.class), замість цього я змінився на @RunWith (MockitoJUnitRunner.class).
ананд криш

20

Для майбутніх читачів ще однією причиною NPE при використанні макетів є забуття ініціалізувати макети так:

@Mock
SomeMock someMock;

@InjectMocks
SomeService someService;

@Before
public void setup(){
    MockitoAnnotations.initMocks(this); //without this you will get NPE
}

@Test
public void someTest(){
    Mockito.when(someMock.someMethod()).thenReturn("some result");
   // ...
}

Також переконайтеся, що ви використовуєте JUnit для всіх анотацій. Одного разу я випадково створив тест з @Test від testNG, тому @Before не працював з ним (у testNG анотація - @BeforeTest)


1
@BeforeEach мені тут допоміг, але це завжди залежить від контексту виконання.
Матеуш Геброскі

1
+1 за "переконайтеся, що ви використовуєте JUnit для всіх анотацій"! Так чи інакше, Intellij припустив, що я хочу використовувати, org.junit.jupiter.api.Testзамість import org.junit.Testчого спричинив макет, мабуть, нульовим.
Саяді

Я якось пропустив цей рядок "MockitoAnnotations.initMocks (this);"
Аджай Кедаре,

У моєму випадку я забув ефективно ініціалізувати свої змінні ДЯКУЮ !!
анонімно

15

Для мене причиною, через яку я отримував NPE, є те, що я використовував, Mockito.any()коли знущався над примітивами. Я виявив, що, переключившись на використання правильного варіанту з mockito, позбавляється помилок.

Наприклад, щоб знущатися над функцією, яка приймає примітив longяк параметр, замість того, щоб використовувати any(), ви повинні бути більш конкретними та замінити це на any(Long.class)або Mockito.anyLong().

Надія, що комусь допомагає.


ДЯКУЮ!!!! Я отримував помилки, які не мали сенсу, але використання Mockito.anyInt (), а не Mockito.any (), вирішило це. Приклад помилки:org.mockito.exceptions.misusing.InvalidUseOfMatchersException: Misplaced or misused argument matcher detected... You cannot use argument matchers outside of verification or stubbing.
Аарон

7

Переконайтеся, що ви ініціалізували свої знущання.

JUnit4 використання @Before

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

JUnit5 використання @BeforeEach

@BeforeEach
public void setup() {
    MockitoAnnotations.initMocks(this);
}

Для JUnit5перевірки ви також використовуєте належний імпорт.

import org.junit.runner.RunWith
import org.mockito.junit.MockitoJUnitRunner;

@RunWith(MockitoJUnitRunner.class)

5

Кутовий випадок:
якщо ви використовуєте Scala і намагаєтесь створити anyзбіг для класу значення , ви отримаєте непотрібний NPE.

Отже, враховуючи case class ValueClass(value: Int) extends AnyVal, що ви хочете зробити, це ValueClass(anyInt)замістьany[ValueClass]

when(mock.someMethod(ValueClass(anyInt))).thenAnswer {
   ...
   val v  = ValueClass(invocation.getArguments()(0).asInstanceOf[Int])
   ...
}

Це інше запитання SO стосується саме цього, але ви пропустите його, коли не знаєте, що проблема в класах значень.


3

вам потрібно ініціалізувати MockitoAnnotations.initMocks (цей) метод повинен бути викликаний для ініціалізації анотованих полів.

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

детальніше див. у документі


Використання org.junit.jupiter.api.BeforeEachinsted of @Beforeдопомогло мені.
RAM237

1
@RunWith(MockitoJUnitRunner.class) //(OR) PowerMockRunner.class

@PrepareForTest({UpdateUtil.class,Log.class,SharedPreferences.class,SharedPreferences.Editor.class})
public class InstallationTest extends TestCase{

@Mock
Context mockContext;
@Mock
SharedPreferences mSharedPreferences;
@Mock
SharedPreferences.Editor mSharedPreferenceEdtor;

@Before
public void setUp() throws Exception
{
//        mockContext = Mockito.mock(Context.class);
//        mSharedPreferences = Mockito.mock(SharedPreferences.class);
//        mSharedPreferenceEdtor = Mockito.mock(SharedPreferences.Editor.class);
    when(mockContext.getSharedPreferences(Mockito.anyString(),Mockito.anyInt())).thenReturn(mSharedPreferences);
    when(mSharedPreferences.edit()).thenReturn(mSharedPreferenceEdtor);
    when(mSharedPreferenceEdtor.remove(Mockito.anyString())).thenReturn(mSharedPreferenceEdtor);
    when(mSharedPreferenceEdtor.putString(Mockito.anyString(),Mockito.anyString())).thenReturn(mSharedPreferenceEdtor);
}

@Test
public void deletePreferencesTest() throws Exception {

 }
}

Усі вищезазначені кодовані коди не потрібні { mockContext = Mockito.mock(Context.class);}, якщо ви використовуєте @Mock Annotation toContext mockContext;

@Mock 
Context mockContext; 

Але це буде працювати, якщо ви використовуєте лише @RunWith (MockitoJUnitRunner.class) . Відповідно до Mockito ви можете створити макетний об'єкт, використовуючи @Mock або Mockito.mock (Context.class); ,

Я отримав NullpointerException через використання @RunWith (PowerMockRunner.class), замість цього я змінився на @RunWith (MockitoJUnitRunner.class), він працює нормально


0

зіткнувся з тією ж проблемою, рішення, яке працювало для мене:

Замість того, щоб глузувати з інтерфейсу служби, я використовував @InjectMocks для глузування над реалізацією служби:

@InjectMocks
private exampleServiceImpl exampleServiceMock;

замість :

@Mock
private exampleService exampleServiceMock;

0

Ед Уебб відповідь допоміг в моєму випадку. І замість цього ви також можете спробувати додати

  @Rule public Mocks mocks = new Mocks(this);

якщо ти @RunWith(JUnit4.class).


0

У моєму випадку я пропустив додавання першим

PowerMockito.spy(ClassWhichNeedToBeStaticMocked.class);

тому це може бути корисним для тих, хто бачить таку помилку

java.lang.NullPointerException
    at org.powermock.api.mockito.internal.expectation.PowerMockitoStubberImpl.addAnswersForStubbing(PowerMockitoStubberImpl.java:67)
    at org.powermock.api.mockito.internal.expectation.PowerMockitoStubberImpl.when(PowerMockitoStubberImpl.java:42)
    at org.powermock.api.mockito.internal.expectation.PowerMockitoStubberImpl.when(PowerMockitoStubberImpl.java:112)

0

Жодна з цих відповідей не спрацювала для мене. Ця відповідь не вирішує проблему OP, але оскільки ця публікація є єдиною, яка з’являється при гугллі цієї проблеми, я ділюсь своєю відповіддю тут.

Я зіткнувся з цією проблемою під час написання модульних тестів для Android. Проблема полягала в тому, що діяльність, яку я тестував, продовжилась, AppCompatActivityа не Activity. Щоб це виправити, я зміг просто замінити AppCompatActivityна, Activityоскільки мені це насправді не було потрібно. Це може бути не життєздатним рішенням для всіх, але, сподіваємось, знання першопричини комусь допоможе.


0

Жодна з наведених відповідей мені не допомогла. Я намагався зрозуміти, чому код працює в Java, але ні на Kotlin.

Потім я зрозумів це з цієї теми .

Ви повинні створити функції класу та члена open, інакше NPE було скинуто.

Після складання функціональних openтестів почали проходити.

Ви також можете розглянути можливість використання "повністю відкритого" модуля компілятора :

У Kotlin за замовчуванням класи та їх члени остаточні , що робить незручним використання фреймворків та бібліотек, таких як Spring AOP, які вимагають відкриття класів . all-openПлагін компілятора пристосовується Котлин вимог цих рамок і робить класи анотовані з конкретними анотаціями та їх члени відкрити без явного відкритого ключового слова.


0

При використанні JUnit 5 або вище. Ви повинні ввести клас, котрий коментується @Mock в @BeforeEachустановці.


0

Інша поширена помилка полягає в тому, що підпис методу випадково оголошується як "остаточний".

Це висвітлює багато людей, які працюють над базами кодів, які піддаються Checkstyle і узагальнили необхідність позначати учасників як final.

тобто у прикладі OP:

object.method()

Переконайтеся, що method()це не оголошено як final:

public final Object method() {
}

Mockito не може знущатися над остаточним методом, і це вийде у вигляді загорнутого NPE:

Suppressed: org.mockito.exceptions.misusing.InvalidUseOfMatchersException:

Поховано глибоко в повідомленні про помилку:

Also, this error might show up because you use argument matchers with methods that cannot be mocked.
Following methods *cannot* be stubbed/verified: final/private/equals()/hashCode().
Mocking methods declared on non-public parent classes is not supported.

0

Оскільки це найближче, що я знайшов до проблеми, яку мав, це перший результат, який з’являється, і я не знайшов відповідної відповіді, я розміщу тут рішення для будь-яких майбутніх бідних душ:

any() не працює там, де метод глузливого класу використовує примітивний параметр.

 public Boolean getResult(String identifier, boolean switch)

Вищезазначене дасть таку саму проблему, що і OP.

Рішення, просто оберніть його:

 public Boolean getResult(String identifier, Boolean switch)

Останній вирішує NPE.


0

У моєму випадку це сталося через неправильний імпорт анотації @Test

Переконайтесь, що ви використовуєте такий імпорт

import org.junit.jupiter.api.Test;
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.