2015-11-12 3 views
2

Я смог использовать тестовую структуру трикотажа для тестирования моего JAX-RS restful api. Однако я не могу заставить EJB правильно издеваться над тем, что я пытаюсь сделать.Mocking EJB with Mockito and Jersey Framework Framework

Используя пример, предоставленный @peeskillet, точка, на которую ссылается мой EJB в ресурсе, остается пустой.

Так что я знаю, что это не мои зависимости. Я считаю, что проблема в том, что я вложен EJB. Есть несколько факторов, которые, я уверен, могут влиять на поведение этого кода, но я не уверен, как обращаться: 1. Моя ApplicationService, которую я пытаюсь высмеять, - это интерфейс, и у меня есть класс ApplicationServiceImpl который имеет в себе фактическую логику. Поскольку мой тест НЕ сфокусирован на этой логике, мой мыслительный процесс насмехался, интерфейс должен быть достаточным. 2. Однако реализация интерфейса также содержит EJB, который подключается к Singleton MongoDB.

Итак, мои вопросы: Может ли какой-либо из этих факторов играть здесь роль в том, почему ссылка на интерфейс в моем классе ресурсов всегда равна нулю, что явно не может насмехаться и вводить? Если да, то как мне это исправить?

Если мне нужно переключиться на издевательство над реализацией напрямую, а не с интерфейсом, то мне также нужно высмеять DAO ejb?

Чтобы быть ясным, работа с тестовым фреймом работает абсолютно нормально, если я не называю какие-либо методы, которые полагаются на поля/объекты, вводимые через EJB. Класс

Мой ресурс


@Path("/stats") 
@Api(value = "stats", authorizations = { 
    @Authorization(value = "apiKey", scopes = {}) 
}) 
@Stateless 
@Produces({MediaType.APPLICATION_JSON}) 
public class StatsResource { 

    private final static Logger LOG = Logger.getLogger(StatsResource.class.getName()); 

    @EJB 
    ApplicationService applicationService; 

    public void setApplicationService(ApplicationService applicationService) { 
     this.applicationService = applicationService; 
    } 

    @GET 
    public String getHere(){ 
     return "Got Here!"; 
    } 

    @GET 
    @Path("test") 
    @ApiOperation(value = "Finds all applications", 
      notes = "Returns brief display which uses a limited subset of Application.class fields", 
      response = Application.class, 
      responseContainer = "List<Application>" 
    ) 
    @JsonView(Views.Brief.class) 
    @Asynchronous 
    public void getApplications(@Suspended 
    final AsyncResponse asyncResponse) throws Exception {   
     asyncResponse.resume(doGetApplications()); 
    } 

    private ApplicationList doGetApplications() throws Exception { 
     return new ApplicationList(applicationService.getAll()); 
    } 

} 

Мой тест Case


public class StatsResourceNGTest extends JerseyTestNg.ContainerPerMethodTest { 
    @Mock 
    private ApplicationService applicationService; 

    @Override 
    protected Application configure() { 
     enable(TestProperties.LOG_TRAFFIC); 
     enable(TestProperties.DUMP_ENTITY); 

     MockitoAnnotations.initMocks(this);   
     Logger.getLogger(StatsResourceNGTest.class.getName()).log(Level.INFO, "Mock App Service: {0}", applicationService.toString()); 
     AbstractBinder binder = new AbstractBinder() { 
      @Override 
      protected void configure() { 
       bindFactory(MockApplicationServiceFactory.class) 
         .to(ApplicationResource.class); 
      } 
     }; 

     ResourceConfig config = new IdentityRestApplication(true); 
     config.register(binder); 
     config.register(StatsResource.class); 

     return config; 
    } 

    @Test 
    public void assertMockInjectionWorked() throws Exception { 
     Response response = target("/stats/test").request(MediaType.APPLICATION_JSON).get(); 

     verify(applicationService, times(1)).getAll(); 
     Logger.getLogger(ApplicationResourceNGTest.class.getName()).log(Level.INFO, "Status: {0}", response.getStatus()); 
     String msg = response.readEntity(String.class); 
     Logger.getLogger(ApplicationResourceNGTest.class.getName()).log(Level.INFO, "Response: {0}", msg); 

     Assert.assertNotNull(msg); 
     response.close(); 

    } 

} 

Мои HK2 MockFactory


public class MockApplicationServiceFactory implements Factory<ApplicationService> { 

private List<Application> appList; 

@Override 
public ApplicationService provide() { 
    appList = new ArrayList<>(); 
    for (int i = 1; i < 6; i++) { 
     String appname = "App " + i; 
     Application app = new ApplicationBuilder() 
       .setName(appname) 
       .setDescription(appname + " Description") 
       .setTenantId(new ObjectId()) 
       .createApplication(); 
     appList.add(app); 
    } 
    try { 
     final ApplicationService mockedService = Mockito.mock(ApplicationService.class); 
     Mockito.when(mockedService.getAll()).thenReturn(appList); 
     return mockedService; 
    } catch (ApplicationException ex) { 
     Logger.getLogger(ApplicationResourceNGTest.class.getName()).log(Level.SEVERE, null, ex); 
    } catch (Exception ex) { 
     Logger.getLogger(ApplicationResourceNGTest.class.getName()).log(Level.SEVERE, null, ex); 
    } 
    return null; 
} 

@Override 
public void dispose(ApplicationService t) { 
} 

}

+0

--redacted-- левый в неправильном месте. –

ответ

0

Я действительно не привел лучший пример. Хотя, Lars Juel Jensen's answer использует TestNG, а не JUnit, настроил его намного чище и обеспечивает лучшую тестируемость

Вот что вы можете сделать. Как juherr mentioned, ApplicationService s - это разные экземпляры. Вместо этого вы можете забыть об Factory. Просто используйте Передразнивало полю

@Mock 
private ApplicationService applicationService; 

@Override 
protected Application configure() { 
    MockitoAnnotations.initMocks(this); 
    ... 
} 

Тогда забудьте о AbstractBinder. Причина в том, что он работает только для аннотаций, которые он знает (т.он не знает @EJB), если только вы не создаете некоторые другие компоненты, чтобы знать аннотацию (но это слишком много). Так просто использовать сеттер, чтобы ввести его

StatsResource resource = new StatsResource(applicationService); 
config.register(resource); 

Теперь в каждом @Test случае, вы можете предоставить совершенно иной макет реализации, то есть ваши wheh(..).then(..) s могут быть различными для каждого теста. Это более гибко, чем поддерживать такую ​​же реализацию на заводе в любом случае.

Итак, в ваших методах @Test просто отработайте класс испытания 'applicationService.

+0

Итак, спасибо за это, похоже, работает. Раньше, когда я пытался использовать аннотации mockito, он, похоже, не работал. На этот раз я протестировал его, убедившись, что моя проверка не выполнена. Я попросил его проверить и посмотреть, был ли вызов, который я издевался над applicationService, вызывался дважды. По крайней мере, он был вызван один раз, поэтому я собираюсь назвать это победой. Однако grizzly возвращает 500 не 200, поэтому я предполагаю, что это еще что-то, что нужно исправлять. Благодаря! –

+0

Оставайтесь _winning_! :-) –

+0

@peeskillet Есть ли у вас какой-либо пример или опыт использования встроенной памяти db и встраиваемого контейнера EJB? –

1
@Mock 
private ApplicationService applicationService; 

и

final ApplicationService mockedService = Mockito.mock(ApplicationService.class); 

2 разные случаи Mock. Таким образом, вы не можете использовать один экземпляр и проверить с другим.

Вам нужно найти способ инициализировать макет один раз и поделиться экземпляром между объектами.

+0

Спасибо. Я этого не заметил. Я пробовал так много разных вещей, некоторые остатки ушли от других усилий. Все еще не решила проблему. –