Подтвердить что ты не робот

Издевательская приватная переменная, предположительно существующая

Как вы можете получить макет объекта во время выполнения, когда он не создан/инициализирован в тестируемом классе, он не является статическим (singleton pattern) или у вас нет какого-либо конструктора тестов,

В классе, для которого я пишу какое-то модульное тестирование, я столкнулся с сценарием, который еще не встречался/не решил. У меня есть ресурс JMS (a QueueConnectionFactory для справки, но это не имеет значения), то есть приватная переменная класса, который я тестирую. Поскольку он имеет аннотацию javax.annotation.Resource, во время выполнения предполагается, что он доступен. Во время тестирования это не так, что создает необходимость издеваться над этим объектом.

Это не статический класс и не используется статическим образом, если бы я мог легко издеваться над использованием различных статических методов издевательства, с которыми я столкнулся. Поскольку ресурс никогда не создается локально (в конструкторе или даже в конструкторе тестов), у меня нет способа передать объект Mock, чтобы во время выполнения теста вместо фактического объекта использовался макет. Как я могу издеваться над этим ресурсом, чтобы при выполнении теста он использовался вместо частного @Resource объекта в классе, который я тестировал?

Для справки, код вызывает createConnection() в QueueConnectionFactory, который бросает исключение нулевого указателя, поскольку Factory не был инициализирован/изделен.

@Stateless
public class Example{
  @Resource(name = "jms/exampleQCF")
  private QueueConnectionFactory queueFactory;

  ...

  public void testMe(){
    Connection connection = queueFactory.createConnection();
    ...
  }
}
4b9b3361

Ответ 1

После многого охоты и поиска всех вариантов, которые мог предложить Mockito/Powermock, я нашел решение (которое я поделюсь в случае, если другие столкнутся с этой проблемой).

Если у вас есть частные переменные-члены, которые никогда не инициализируются (и просто предполагается, что они созданы в других местах), вы можете использовать аннотацию @InjectMocks для "ввода" Mocks, которые вы хотите, в свой класс, который вы тестируете.

  • Добавьте переменную в тестовый класс для тестируемого вами класса и дайте ей аннотацию @InjectMocks (org.Mockito.InjectMocks).
  • Используйте @Mock аннотации для настройки макетов, которые вы хотите ввести. Используйте свойство name @Mock (name = "privateVariableNameHere") для сопоставления объекта Mock с частной переменной внутри вашего класса, который вы тестируете.
  • В функции настройки или перед вызовом вашего класса инициализируйте mocks. Самый простой способ, который я нашел, - использовать метод "setup" с аннотацией @Before. Затем внутри там вызовите MockitoAnnotations.initMocks(this);, чтобы быстро инициализировать что-либо с помощью аннотации @Mock.
  • Определите свою функциональность Mock в методе тестирования (перед вызовом метода, который вы тестируете).
  • Используя объект @InjectMock, вызовите ваш метод, который вы тестируете... издевательствам ДОЛЖНЫ быть подключены и работать как определено на более ранних этапах.

Итак, для класса класса, который я использовал выше, код для проверки /mock имел бы Connection, возвращаемый как макет, который вы можете делать с помощью. Основываясь на приведенном выше примере в моем вопросе, это будет выглядеть так:

@RunWith(PowerMockRunner.class)
@PrepareForTest({/* Static Classes I am Mocking */})
public class ExampleTest {
  @Mock (name = "queueFactory") //same name as private var.
  QueueConnectionFactory queueFactoryMock;
  @Mock
  Connection connectionMock; //the object we want returned
  @InjectMocks
  Example exampleTester; //the class to test

  @Before
  public void setup(){
    MockitoAnnotations.initMocks(this); // initialize all the @Mock objects
    // Setup other Static Mocks
  }

  @Test
  public void testTestMe(){
    //Mock your objects like other "normally" mocked objects
    PowerMockito.when(queueFactoryMock.createConnection()).thenReturn(connectionMock);
    //...Mock ConnectionMock functionality...
    exampleTester.testMe();
  }
}

Ответ 2

Несколько подходов здесь:

  1. ReflectionTestUtils платформы Spring Testing: ReflectionTestUtils.setField(objectToTest, "privateFieldName", mockObjectToInject);. При этом вы не вводите другую зависимость.
  2. org.mockito.internal.util.reflection.FieldSetter.
  3. PowerMock.Whitebox.setInternalState() издеваться над приватным полем.

Если вам нужно создать внутреннюю локальную переменную, используйте PowerMockito.whenNew(Foo.class).withNoArguments().thenReturn(foo);. Очень, очень полезно. Не могу найти другие способы сделать то же самое.

Используя только Mockito, вы не можете имитировать создание локальной переменной, потому что when(any(Foo.class) не работает; вернет ноль. Компилируется, но не работает.

Рекомендации: Mockito: фиктивная инициализация частного поля