Jersey - How to mock service

前端 未结 2 977
你的背包
你的背包 2020-11-27 19:31

I am using \"Jersey Test Framework\" for unit testing my webservice.

Here is my resource class :

import javax.ws.rs.GET;
import javax.ws.rs.Path;
imp         


        
相关标签:
2条回答
  • 2020-11-27 20:06

    See Update below: You don't need a Factory


    If you are using Jersey 2, one solution would be to use Custom Injection and Lifecycle Management feature (with HK2 - which comes with the Jersey dist). Also required would be a Mocking framework of course. I'm going to use Mockito.

    First create a Factory with mocked instance:

    public static interface GreetingService {
        public String getGreeting(String name);
    }
    
    public static class MockGreetingServiceFactory 
                                         implements Factory<GreetingService> {
        @Override
        public GreetingService provide() {
            final GreetingService mockedService
                    = Mockito.mock(GreetingService.class);
            Mockito.when(mockedService.getGreeting(Mockito.anyString()))
                    .thenAnswer(new Answer<String>() {
                        @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) {}
    }
    

    Then use the AbstractBinder to bind the factory to the interface/service class, and register the binder. (It's all described in the link above):

    @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;
    }
    

    Seems like a lot, but it's just an option. I'm not too familiar with the test framework, or if it has an mocking capabilities for injection.

    Here is the full test:

    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<GreetingService> {
            @Override
            public GreetingService provide() {
                final GreetingService mockedService
                        = Mockito.mock(GreetingService.class);
                Mockito.when(mockedService.getGreeting(Mockito.anyString()))
                        .thenAnswer(new Answer<String>() {
                            @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();
           
        }
    }
    

    Dependencies for this test:

    <dependency>
        <groupId>org.glassfish.jersey.test-framework.providers</groupId>
        <artifactId>jersey-test-framework-provider-grizzly2</artifactId>
        <version>2.13</version>
    </dependency>
    <dependency>
        <groupId>org.mockito</groupId>
        <artifactId>mockito-all</artifactId>
        <version>1.9.0</version>
    </dependency>
    

    UPDATE

    So in most cases, you really don't need a Factory. You can simply bind the mock instance with its contract:

    @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
    }
    

    Much simpler!

    0 讨论(0)
  • 2020-11-27 20:24

    Here is how I did it with Jersey 2.20, Spring 4.1.4 RELEASE, Mockito 1.10.8, and 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>(caseConverter)).to(CaseConverter.class);
                    bindFactory(new InstanceFactory<CaseService>(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<Case> cases = target("/cases").request().get(new GenericType<Collection<Case>>(){});
    
            verify(caseService, times(1)).getAll();
            verify(caseConverter, times(1)).convertToApi(any(solve.scm.domain.Case.class));
    
            assertThat(cases).hasSize(1);
        }
    }
    

    You also need this class which makes the binding code above a bit easier:

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

    Edited as pr. request. This is the contents of my test-context.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
    </beans>
    

    It turns out that my test-context.xml does not instantiate any beans nor scan any packages, in fact, it does not do anything at all. I guess I just put it there in case I might need it.

    0 讨论(0)
提交回复
热议问题