如何用JUnit测试一个REST应用编程接口


您是否想要尽快且没有任何麻烦地测试您的睡觉应用编程接口?代码中可以进行测试的最早位置是在提交阶段,您可以在此阶段运行单元测试。我想为您介绍一种简单快捷的测试睡觉接口的方法。示例代码基于JUnit和RESTEasy。它不需要任何外部数据库或重量级应用程序服务器,这意味着您可以独立运行并开箱即用。

RESTEasy(和Jersey一样)在他们的库中包含一个最小的Web服务器,使他们的用户能够启动一个小型Web服务器,用于生产或运行测试。

TJWSEmbeddedJaxrsServer server = new TJWSEmbeddedJaxrsServer();
server.setPort("12345");

server.getDeployment().getResources().add(myResourceInstance);

// alternative
server.getDeployment().getProviderClasses().add("com.my.my.resource.class.name");
server.start();
服务器将在没有任何上下文前缀的情况下装载您的资源。您已准备好开始测试。


new ResteasyClientBuilder().build().target("http://localhost:12345/myresource").request().get()


就这样。有些测试用例就是这么简单。其他测试用例需要更多功能,如动态端口(例如,当多个构建在同一台机器上运行时)、用于身份验证的安全域或您需要的一组提供程序类,以便编组/解组/转换异常。这些要求可能会导致每次测试中出现大量样本。我想提供一种更通用、更可重用的方法,用Junit和RESTEasy 3.0

public class InMemoryRestTest {
@Path("myresource")
    public static class MyResource {

        @POST
        @Consumes(MediaType.TEXT_PLAIN)
        @Produces(MediaType.APPLICATION_XML)
        public MyModel createMyModel(int number) {

            return new MyModel(number);
        }

    }

    public static MyResource sut = new MyResource();
    public static InMemoryRestServer server;

    @BeforeClass
    public static void beforeClass() throws Exception {
        server = InMemoryRestServer.create(sut);
    }

    @AfterClass
    public static void afterClass() throws Exception {
        server.close();
    }

    @Test
    public void postSimpleBody() throws Exception {

        Response response = server.newRequest("/myresource").request().buildPost(Entity.text("42")).invoke();
        assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());

        MyModel myModel = response.readEntity(MyModel.class);
        assertEquals(42, myModel.getResult());
    }
}

这个测试已经构建好了一切,可以提供睡觉应用编程接口并在一个单元中使用它。MyResource是睡觉服务提供功能,RESTEasy客户端利用该接口。

所有引导和睡觉服务器设置都封装在InMemoryRestServer这也为测试提供了一个方便的入口点。您不再需要在测试代码中处理端口号。你可以找到完整的代码here

有时,您的代码依赖于位于其他类内或接口后面的其他服务或功能。通常,您会使用模拟来模拟该行为。所以让我们试一试。

@RunWith(MockitoJUnitRunner.class)
public class InMemoryRestWithMockitoTest {

    public static interface BackendService {

        MyModel getMyModel(int number);
    }

    @Path("myresource")
    public static class MyResource {

        private BackendService backendService;

        @POST
        @Consumes(MediaType.TEXT_PLAIN)
        @Produces(MediaType.APPLICATION_XML)
        public MyModel createMyModel(int number) {

            return backendService.getMyModel(number);
        }

    }

    @InjectMocks
    public static MyResource sut = new MyResource();
    public static InMemoryRestServer server;

    @Mock
    private BackendService backendServiceMock;

    @BeforeClass
    public static void beforeClass() throws Exception {
        server = InMemoryRestServer.create(sut);
    }

    @AfterClass
    public static void afterClass() throws Exception {
        server.close();
    }

    @Test
    public void postWithoutMocking() throws Exception {

        Response response = server.newRequest("/myresource").request().buildPost(Entity.text("42")).invoke();
        assertEquals(Response.Status.NO_CONTENT.getStatusCode(), response.getStatus());
    }

    @Test
    public void postWithMocking() throws Exception {

        when(backendServiceMock.getMyModel(42)).thenReturn(new MyModel(42));

        Response response = server.newRequest("/myresource").request().buildPost(Entity.text("42")).invoke();
        assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());

        MyModel myModel = response.readEntity(MyModel.class);
        assertEquals(42, myModel.getResult());
    } 
  }

这段代码并不比没有模仿的代码复杂多少。您可以使用您选择的模仿框架(这里我使用Mockito),将模拟注入您的睡觉应用编程接口服务并运行您的测试。您可以在上找到所有代码GitHub