Использование JMockit для фальсификации реализаций реализованных интерфейсов

Мы пишем тесты JUnit для класса, который использует Spring autowiring для инъекции зависимости, которая является некоторым экземпляром интерфейса. Поскольку тестируемый класс никогда явно не задает зависимость или не передал ее в конструкторе, похоже, что JMockit не считает необходимым также создавать его.

До сих пор мы использовали SpringRunner для загрузки Spring нагрузок для нас, что работает. Две вещи, которые нам не нравятся в этом: 1) инфраструктура Spring должна быть загружена и инициализирована каждый раз при запуске тестов, которые не совсем быстрые, и 2) мы вынуждены явно создавать все макетные зависимости как реальные классы, то, что помогает JMockit.

Здесь приведен упрощенный пример того, что мы тестируем:

public class UnitUnderTest {

   @Autowired
   ISomeInterface someInterface;

   public void callInterfaceMethod() {

      System.out.println( "UnitUnderTest.callInterfaceMethod calling someInterface.doSomething");
      someInterface.doSomething();
   }

}

Итак, вопрос в том, есть ли способ заставить JMockit создать mock someInterface?

Ответ 1

JMockit всегда будет создавать экземпляр измеченного интерфейса (за исключением случая последнего фиктивного поля), но это происходит только в тестовом коде. Он не будет автоматически вставлять экземпляр в тестируемый код.

Вам нужно будет вручную ввести экземпляр mock. Например:

public class SomeTest
{
   @Autowired UnitUnderTest unitUnderTest;
   @Mocked ISomeInterface theMock; // created and assigned automatically

   @Test
   public void testSomeMethod()
   {
      Deencapsulation.setField(unitUnderTest, theMock);
      //proceed with unit test here
   }
}

mockit.Deencapsulation - это класс утилиты, основанный на отражении, который позволяет вам вызывать частные методы, получать/устанавливать поля и т.д.

Ответ 2

Вы можете использовать org.springframework.test.util.ReflectionTestUtils для явного вложения вашего изделенного ISomeInterface в тестовый файл.

См. документация

Ответ 3

С подсказками, любезно предоставленными выше, вот что я нашел наиболее полезным, как кто-то довольно новый для JMockit: JMockit предоставляет класс Deencapsulation, позволяющий вам устанавливать значения частных зависимых полей (нет необходимости перетаскивать Spring библиотеки) и класс MockUp, который позволяет явно создать реализацию интерфейса и высмеять один или несколько методов интерфейса. Вот как я решил решить этот конкретный случай:

@Before
public void setUp() {

   IMarketMakerDal theMock = new MockUp <IMarketMakerDal>() {

      @Mock
      MarketMakerDcl findByMarketMakerGuid( String marketMakerGuid ) {

         MarketMakerDcl marketMakerDcl = new MarketMakerDcl();
         marketMakerDcl.setBaseCurrencyCode( CURRENCY_CODE_US_DOLLAR );
         return marketMakerDcl;
      }
   }.getMockInstance();

   setField( unitUnderTest, theMock );
}

Спасибо всем за помощь.

Ответ 4

Для тех людей, которые встречались

java.lang.IllegalStateException: Missing @Injectable for field *** 

или

java.lang.IllegalStateException: Missing @Tested class for field ***

при использовании jmockit, чтобы издеваться над @autowired в рамках spring (или spring boot), я сделал менее двух шагов, чтобы избежать ошибок выше:

  • используйте @Tested(fullyInitialized=true) вместо @Tested

https://groups.google.com/forum/#!msg/jmockit-users/uo0S51lSX24/lQhLNN--eJcJ

  1. верните версию jmockit обратно в 1.18 или предыдущие

https://groups.google.com/forum/#!topic/jmockit-users/wMFZggsA8LM

Ответ 5

Если у вас есть аннотация @Qualifier для интерфейса, вам нужно указать поле @Injectable точно так же, как оно указано в определителе.

Вот цитата из JMockit doc:

Пользовательские имена, указанные в аннотациях полей из Java EE (@Resource (имя), @Named) или структуры Spring (@Qualifier), используются при поиске соответствующего значения @Injectable или @Tested. Когда такое имя содержит a - (тире) или. (точка), вместо этого используется соответствующее имя с верблюжьим именем.

Например:

@Component
public class AClass {

   @Autowired
   private Bean1 bean1;

   @Autowired
   @Qualifier("my-dashed-name")
   private AmqpTemplate rpcTemplate;
}

Unit test класс:

public class AClassTest {

   @Injectable
   private Bean1 bean1;

   @Injectable
   private AmqpTemplate myDashedName;

   @Tested
   private AClass aClass = new AClass();
}

Также нет необходимости использовать setFiled для каждого @Autowired bean, все поля автоматически вводятся при создании экземпляра класса @Tested. Протестировано на JMockit ver. 1,30