Как насмехаться над сервисами с Аркиллиан?

Можно ли использовать какую-то насмешливую фреймворк с Аркиллиан, или точно, как насмехаться с введенными EJB? Я знаю, что с использованием CDI (Contexts and Dependency Injection) можно вводить альтернативы в тест. Но без CDI в качестве механизма впрыска, когда я использую только инъекцию EJB, как это возможно?

Недавно я проверил свои EJB с помощью mock-реализации служебного интерфейса следующим образом:

// Service inteface 
public interface Audit {
   void audit(String info);
}

// Mock implementation
@Stateless
public class MockAuditBean implements Audit {

    public static String lastInfo = null;

    @Override
    public void audit(String info) {
        this.lastInfo = info;
    }
}

// assert in test
assertTrue(MockAuditBean.lastInfo.contains("dummy"));

Этот подход возможен, но требует множества пользовательских макетных реализаций. Что еще хуже, инъецированные экземпляры mocks являются прокси-серверами и используют сервисный интерфейс. Их нельзя отбрасывать, чтобы высмеять класс реализации для сравнения результатов. Могут использоваться только статические элементы и методы макетной реализации.

Я также проверил еще одну возможность установки связанных EJB вручную. Этот подход имеет несколько отклонений. Для этого требуется, чтобы целевой EJB теста имел для них не-частные члены или сеттеры. Когда целевой EJB опирается на аннотацию жизненного цикла @PostConstruct, вы должны вызвать его после настройки ручного "инъекции". Преимуществом этого решения является возможность использования макетных фреймворков, таких как mockito или jMock.

Попросите кого-нибудь поделиться опытом, как тестировать и настраивать такой интеграционный тест, или даже использовать в нем насмешливые фреймворки?

Ответы

Ответ 1

IMO, EJB, которые не предназначены для тестирования. Ваша альтернатива звучит как довольно хороший компромисс, и я бы пошел на это. Использование mockito является основным плюсом, и я использую его даже при работе с CDI.

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

Ответ 2

В этой статье из Oracle показан подход к "инъекции" EJB для тестирования с использованием JUnit и Mockito: http://www.oracle.com/technetwork/articles/java/unittesting-455385.html

Изменить: В принципе, включение Mockito позволяет издеваться над такими объектами, как EntityManager и т.д.:

import static org.mockito.Mockito.*;

...

em = mock(EntityManager.class);

Они показывают подход для EJB, также используя mockito. Учитывая EJB:

@Stateless
public class MyResource {
 @Inject
 Instance<Consultant> company;

 @Inject
 Event<Result> eventListener;

Тест может "вводить" те объекты:

public class MyResourceTest {

 private MyResource myr;
 @Before
 public void initializeDependencies(){
 this.myr = new MyResource();
 this.myr.company = mock(Instance.class);
 this.myr.eventListener = mock(Event.class);
 }

Обратите внимание, что MyResource и MyResource находятся в том же пути класса, но в разных исходных папках, поэтому ваши тесты имеют доступ к защищенным полям, company и eventListener.


Edit:

Примечание: вы можете использовать FacesMockitoRunner из JBoss (https://community.jboss.org/thread/170800), чтобы сделать это для общих компонентов JSF и использовать аннотации для другие (Java EE 6 с CDI включен в качестве предварительного условия для этого, но не требует сервера JBoss):

  • Включить зависимости jsf, mockito и jsf-mockito в maven:

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-core</artifactId>  
            <version>1.9.5</version> 
            <scope>test</scope>
        </dependency>
        <dependency>
          <groupId>org.jboss.test-jsf</groupId>
          <artifactId>jsf-mockito</artifactId>
          <version>1.1.7-SNAPSHOT</version>
          <scope>test</scope>
        </dependency>
    
  • Добавьте аннотацию @RunWith к вашему тесту:

    @RunWith(FacesMockitoRunner.class)
    public class MyTest {
    
  • Вложение общих объектов Faces с помощью аннотаций:

    @Inject
    FacesContext facesContext;
    @Inject
    ExternalContext ext;
    @Inject
    HttpServletRequest request;
    
  • Отметьте любые другие объекты с помощью аннотаций @org.mockito.Mock (он появляется FacesMockitoRunner вызывает это за кулисами, поэтому здесь может быть не нужно):

    @Mock MyUserService userService;
    @Mock MyFacesBroker broker;
    @Mock MyUser user;
    
  • Запустите Injected Mocks с помощью

    @Before public void initMocks() {
        // Init the mocks from above
        MockitoAnnotations.initMocks(this);
    }
    
  • Установите свой тест как обычно:

    assertSame(FacesContext.getCurrentInstance(), facesContext);
    when(ext.getSessionMap()).thenReturn(session);
    assertSame(FacesContext.getCurrentInstance().getExternalContext(), ext);
    assertSame(FacesContext.getCurrentInstance().getExternalContext().getSessionMap(), ext.getSessionMap());
    

и др.

Ответ 3

Вы можете взглянуть на testfun-JEE, который позволяет вам тестировать (не интегрировать-тест) ваши EJB за пределами контейнер. testfun-JEE заботится о том, чтобы вводить EJB, а также EntityManager и некоторый стандартный ресурс непосредственно в ваш тестовый класс - ссылки в этих EJB на другие EJB разрешаются автоматически.

И самое крутое, что вы можете издеваться над любой зависимостью, просто добавив переменную-член к вашему тесту, аннотированную с помощью @Mock - testfun-JEE будет вводить этот макет, где нужно.

См. примеры в https://github.com/michaelyaakoby/testfun.

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

Ответ 4

Если вы действительно хотите взаимодействовать с mocks в своих тестах интеграции (например, одна из причин может заключаться в том, что вы еще не выполняете полномасштабную реализацию или у вас есть внешний вид внешних систем, в которых у вас нет контроля), существует довольно простой способ интегрировать Mockito с вашими испытаниями Arquillian, посмотрите этот пример из витрины. Он фактически расширяет сам по себе, но не выпускается как один.

Ответ 5

Работайте с каркасом, например, Mockito.

К сожалению, Arquillian автоматически не включает необходимые зависимости. Вы можете добавить их в свою функцию @Deployment:

@Deployment  
public static WebArchive deploy()  
{  
  return ShrinkWrap.create(WebArchive.class)  
        .addAsLibraries( // add maven resolve artifacts to the deployment  
            DependencyResolvers.use(MavenDependencyResolver.class)  
            .artifact("org.mockito:mockito-all:1.8.3")  
            .resolveAs(GenericArchive.class))  
        );  
}  

источник

Затем в вашем методе @Test вы можете использовать:

mock(MockedService.class).methodName()

Эта демонстрация github показывает способ автоматического обнаружения, который, как представляется, требует некоторой настройки: источник