Jersey – Cómo burlarse del servicio

Estoy usando “Jersey Test Framework” para probar mi unidad de servicio web.

Aquí está mi clase de recursos:

import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; // The Java class will be hosted at the URI path "/helloworld" @Path("/helloworld") public class class HelloWorldResource { private SomeService service; @GET @Produces("text/plain") public String getClichedMessage() { // Return some cliched textual content String responseFromSomeService = service.getSomething(); return responseFromSomeService; } } 

¿Cómo puedo simular SomeService en pruebas unitarias?

Así es como lo hice con Jersey 2.20, Spring 4.1.4 RELEASE, Mockito 1.10.8 y TestNG 6.8.8.

 @Test public class CasesResourceTest extends JerseyTestNg.ContainerPerMethodTest { @Mock private CaseService caseService; @Mock private CaseConverter caseConverter; @Mock private CaseRepository caseRepository; private CasesResource casesResource; @Override protected Application configure() { MockitoAnnotations.initMocks(this); casesResource = new CasesResource(); AbstractBinder binder = new AbstractBinder() { @Override protected void configure() { bindFactory(new InstanceFactory(caseConverter)).to(CaseConverter.class); bindFactory(new InstanceFactory(caseService)).to(CaseService.class); } }; return new ResourceConfig() .register(binder) .register(casesResource) .property("contextConfigLocation", "solve-scm-rest/test-context.xml"); } public void getAllCases() throws Exception { when(caseService.getAll()).thenReturn(Lists.newArrayList(new solve.scm.domain.Case())); when(caseConverter.convertToApi(any(solve.scm.domain.Case.class))).thenReturn(new Case()); Collection cases = target("/cases").request().get(new GenericType>(){}); verify(caseService, times(1)).getAll(); verify(caseConverter, times(1)).convertToApi(any(solve.scm.domain.Case.class)); assertThat(cases).hasSize(1); } } 

También necesita esta clase que hace que el código de enlace sea un poco más fácil:

 public class InstanceFactory implements Factory { private T instance; public InstanceFactory(T instance) { this.instance = instance; } @Override public void dispose(T t) { } @Override public T provide() { return instance; } } 

Editado como pr. solicitud. Este es el contenido de mi test-context.xml:

 < ?xml version="1.0" encoding="UTF-8"?>   

Resulta que mi test-context.xml no instancia ningún frijol ni escanea ningún paquete, de hecho, no hace nada en absoluto. Supongo que lo puse en caso de que lo necesite.

Consulte la actualización a continuación: No necesita una Factory


Si está utilizando Jersey 2, una solución sería utilizar la función de inyección personalizada y gestión del ciclo de vida (con HK2 , que viene con el dist de Jersey). También se requiere un marco de burla, por supuesto. Voy a usar Mockito.

Primero crea una fábrica con instancia simulada:

 public static interface GreetingService { public String getGreeting(String name); } public static class MockGreetingServiceFactory implements Factory { @Override public GreetingService provide() { final GreetingService mockedService = Mockito.mock(GreetingService.class); Mockito.when(mockedService.getGreeting(Mockito.anyString())) .thenAnswer(new Answer() { @Override public String answer(InvocationOnMock invocation) throws Throwable { String name = (String)invocation.getArguments()[0]; return "Hello " + name; } }); return mockedService; } @Override public void dispose(GreetingService t) {} } 

Luego use AbstractBinder para vincular la fábrica a la clase de interfaz / servicio, y registre la carpeta. (Todo se describe en el enlace de arriba):

 @Override public Application configure() { AbstractBinder binder = new AbstractBinder() { @Override protected void configure() { bindFactory(MockGreetingServiceFactory.class) .to(GreetingService.class); } }; ResourceConfig config = new ResourceConfig(GreetingResource.class); config.register(binder); return config; } 

Parece mucho, pero es solo una opción. No estoy muy familiarizado con el marco de prueba, o si tiene una capacidad de burla para la inyección.

Aquí está la prueba completa:

 import javax.inject.Inject; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.client.Client; import javax.ws.rs.client.ClientBuilder; import javax.ws.rs.core.Application; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import org.glassfish.hk2.api.Factory; import org.glassfish.hk2.utilities.binding.AbstractBinder; import org.glassfish.jersey.server.ResourceConfig; import org.glassfish.jersey.test.JerseyTest; import org.junit.Assert; import org.junit.Test; import org.mockito.Mockito; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; public class ServiceMockingTest extends JerseyTest { @Path("/greeting") public static class GreetingResource { @Inject private GreetingService greetingService; @GET @Produces(MediaType.TEXT_PLAIN) public String getGreeting(@QueryParam("name") String name) { return greetingService.getGreeting(name); } } public static interface GreetingService { public String getGreeting(String name); } public static class MockGreetingServiceFactory implements Factory { @Override public GreetingService provide() { final GreetingService mockedService = Mockito.mock(GreetingService.class); Mockito.when(mockedService.getGreeting(Mockito.anyString())) .thenAnswer(new Answer() { @Override public String answer(InvocationOnMock invocation) throws Throwable { String name = (String)invocation.getArguments()[0]; return "Hello " + name; } }); return mockedService; } @Override public void dispose(GreetingService t) {} } @Override public Application configure() { AbstractBinder binder = new AbstractBinder() { @Override protected void configure() { bindFactory(MockGreetingServiceFactory.class) .to(GreetingService.class); } }; ResourceConfig config = new ResourceConfig(GreetingResource.class); config.register(binder); return config; } @Test public void testMockedGreetingService() { Client client = ClientBuilder.newClient(); Response response = client.target("http://localhost:9998/greeting") .queryParam("name", "peeskillet") .request(MediaType.TEXT_PLAIN).get(); Assert.assertEquals(200, response.getStatus()); String msg = response.readEntity(String.class); Assert.assertEquals("Hello peeskillet", msg); System.out.println("Message: " + msg); response.close(); client.close(); } } 

Dependencias para esta prueba:

  org.glassfish.jersey.test-framework.providers jersey-test-framework-provider-grizzly2 2.13   org.mockito mockito-all 1.9.0  

ACTUALIZAR

Entonces, en la mayoría de los casos, realmente no necesita una Factory . Simplemente puede vincular la instancia simulada con su contrato:

 @Mock private Service service; @Override public ResourceConfig configure() { MockitoAnnotations.initMocks(this); return new ResourceConfig() .register(MyResource.class) .register(new AbstractBinder() { @Override protected configure() { bind(service).to(Service.class); } }); } @Test public void test() { when(service.getSomething()).thenReturn("Something"); // test } 

¡Mucho más simple!