@InjectMocks, конструктор или блок инициализации выдали исключение

Когда я использую @InjectMocks, произошло исключение. Мой код показан ниже:

class A {
    private X x;
    private Y y;
    public A(String ip, int port) {
       this(someMethodCall(ip, port)); //
    }

    private A(X x) {
        this.x = x;
        this.y = new Y();
    }
}
UT:
public class ATest() {
    @InjectMocks A a;
    @Mock X x;
    @Mock Y y;
    @Test ...
}

это вызовет NPE, может кто-нибудь мне помочь?

org.mockito.exceptions.base.MockitoException: Cannot instantiate @InjectMocks field named 'channel' of type 'class Juinit3.Channel'. You haven't provided the instance at field declaration so I tried to construct the instance. However, the constructor or the initialization block threw an exception: null.


person dingrui    schedule 24.11.2017    source источник


Ответы (3)


О чем говорит вам это исключение...

Вы не указали экземпляр при объявлении поля

Другими словами, вы не написали...

@InjectMocks 
A a = new A("foobar", 123);

Это было бы вполне приемлемо и, вероятно, решит вашу проблему. Пожалуйста, помните, что макеты НЕ будут инициализированы в этот момент, так что это нормально, если вам действительно нужны примеры String и int, но не в том случае, если вам нужно поместить туда макеты. Другими словами, если бы у вас был конструктор, который принимал X, и вы бы написали здесь новое A(x), x было бы нулевым, поскольку аннотация @Mock еще не была обработана.

поэтому я попытался построить экземпляр

Поскольку экземпляра не было (потому что вы его не предоставили), он попытался создать его, но...

Однако конструктор или блок инициализации выдали исключение: null

Итак, ваш конструктор выдает null. Похоже, что ваш someMethodCall зависит от аргументов (скорее всего, порта), которые не равны нулю, но, поскольку они String и int, Mockito понятия не имеет, какие значения там использовать. Поскольку port является примитивным типом, а Mockito не обрабатывает их конкретно, проблема, вероятно, в нем — Mockito попытается поместить туда null, что вызовет исключение.

Например, если ваш конструктор соответствует X и Y, Mockito, вероятно, попытается поместить туда макеты, но это не так. Конструктору нужны String и int, и для них нет макетов, поэтому Mockito может использовать только значения по умолчанию, а это null, что является проблемой в случае port (из-за int).

Итак, каково решение?

1) Либо сделайте свой конструктор нулевым, позволяя указать там нулевой порт (и убедитесь, что строка ip также обрабатывается нулевым безопасным способом).

2) Используйте то, чем не пользовались:

@InjectMocks 
A a = new A("foobar", 123);

В любом случае, не обязательно иметь все зависимости в конструкторе, Mockito отлично впишет их в поля напрямую. Поэтому добавление еще одного конструктора для X и Y не является реальным решением. Конечно, как правило, внедрение конструктора предпочтительнее внедрения поля, но это уже другая тема.

Что касается вашего вопроса о том, какой конструктор: документация говорит об этом...

выбирается самый большой конструктор, затем аргументы разрешаются только с помощью моков, объявленных в тесте

Редактировать: кажется, что Mockito не знает, как обрабатывать примитивные поля в конструкторах, какой позор.

person Florian Schaetz    schedule 24.11.2017
comment
Спасибо за ваш ответ. - person dingrui; 24.11.2017
comment
Я закончил с решением, описанным в tedvinke.wordpress.com/2014/02/13/ - person pedrorijo91; 29.01.2020

Проблема с вашим полем @InjectMocks. Поскольку вы не инициализировали его напрямую следующим образом:

@InjectMocks A a = new A("localhost", 80);

mockito попытается выполнить инициализацию конструктора. В этом случае он выберет самый большой конструктор. В вашем случае это public A(String ip, int port). Если нет фиктивных полей, которые будут соответствовать аргументам конструктора, mockito передаст nulls в качестве значений для выбранного конструктора. Так что в этом случае экземпляр инициализируется как new A(null, null). В этом случае вы получите NPE, так как вторым параметром вашего конструктора является int, и когда null будет распакован в int, будет выброшено NPE.

person Sergii Bishyr    schedule 24.11.2017

1) Либо создайте общедоступный конструктор без аргументов, либо создайте конструктор public A(X x, Y y).

2) Убедитесь, что вы используете

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

or

@Before
public void init(){
  MockitoAnnotations.initMocks(this);
}
person Maciej Kowalski    schedule 24.11.2017
comment
Я хочу задать еще один вопрос. Какой конструктор будет вызываться при создании экземпляра поля @InjectMocks? Есть какой-то порядок? Я пробовал конструктор с параметрами и конструктор без параметров, но обнаружил, что у вызова нет правил. - person dingrui; 24.11.2017
comment
2) не может быть здесь проблемой, поскольку, если бы их не было, никто бы ничего не делал с @InjectMocks и, следовательно, не было бы такого исключения, поэтому мы можем сделать вывод, что одно из этих двух присутствует. - person Florian Schaetz; 24.11.2017
comment
Также проблема не в том, что x и y не заданы в конструкторе, поскольку Mockito отлично работает с внедрением поля, поэтому вторая половина 1) тоже не может быть решением. Конструктор по умолчанию будет работать, но я бы не стал добавлять его только для тестов. - person Florian Schaetz; 24.11.2017