joninx
joninx

Reputation: 1780

How to test a restful web service in java

I've written a web service at work and now I want to write some tests. I'm doubtful as how to test them, but I know which tools I need: JUnit 4 and perhaps an embedded application container, for instance Tomcat, plus Jersey Client. Nevertheless, here you are my approaches:

  1. Create a test class where an embedded Application Container (Tomcat) is started. Then, deploy the web service to it and make test calls using Jersey Client. This is the way I've done it so far. The problem comes here: to deploy the web service, I must have the WAR created previously. This is a problem, as when building the application with Maven firstly tests are executed and then WAR is built; I think this is actually a loop. Would it be possible to deploy my webapp without the need of the war? Here you are my test class:

    public class TestAutenticacionService {
    
        private final String mWorkingDir = "C://Users//Asasa//workspace//myWebApp//";
        private Tomcat tomcat = null;
        private WebTarget webTarget = null;
        private ObjectMapper mapper = new ObjectMapper();
        private Long idClienteCorrecto = 787538L;   
    
        @Before
        public void setUp() throws Exception {  
            tomcat = new Tomcat();
            tomcat.setPort(9090);
            tomcat.setBaseDir(mWorkingDir);
            tomcat.getHost().setAppBase(mWorkingDir);
            tomcat.getHost().setAutoDeploy(true);
            tomcat.getHost().setDeployOnStartup(true);
            tomcat.getServer().addLifecycleListener(new VersionLoggerListener());
            tomcat.getHost().addLifecycleListener(new HostConfig());
    
            try {
                tomcat.addWebapp("/bidegiWsCli", "C:/Users/Asasa/workspace/myWebApp/myWebApp.war");
                tomcat.start();
                System.out.println("Tomcat iniciado en " + tomcat.getHost());
    
                webTarget = buildClientWs();
    
            } catch (LifecycleException e) {
                System.err.println("Tomcat no se pudo iniciar.");
                e.printStackTrace();
            }
        }
    
        @After
        public void tearDown() throws Exception {
            if(tomcat != null){
                try {
                    tomcat.stop();
                    System.out.println("Tomcat parado.");
                } catch (LifecycleException e) {
                    System.err.println("Error al intentar parar Tomcat.");
                    e.printStackTrace();
                }   
            }
        }
    
        @Test
        public void test() {
    
            WebTarget loginTgt = webTarget.path("login");
    
            // Probamos un login correcto:
            WebTarget loginTgtOk = loginTgt.queryParam("Aus", "764577676t").queryParam("Pass", "****");
            Response respOk = loginTgtOk.request(MediaType.APPLICATION_JSON).get();
            String strLrOk = (String) respOk.readEntity(String.class);
            try {
                LoginResponse lrOk = mapper.readValue(strLrOk, LoginResponse.class);
                Long idClienteOk = lrOk.getIdCliente();
                assertEquals(idClienteOk, idClienteCorrecto);
            } catch (Exception e) {
                try {
                    mapper.readValue(strLrOk, ExceptionResponse.class);
                    assertTrue(true);
                } catch (JsonParseException e1) {
                    System.err.println("Error JsonParseException: " + e1.getMessage());
                    e1.printStackTrace();
                } catch (JsonMappingException e1) {
                    System.err.println("Error JsonMappingException: " + e1.getMessage());
                    e1.printStackTrace();
                } catch (IOException e1) {
                    System.err.println("Error IOException: " + e1.getMessage());    
                    e1.printStackTrace();
                }
            }
    
            // Probamos un login incorrecto:
            WebTarget loginTgtWrong = loginTgt.queryParam("Aus", "764577676t").queryParam("Password", "***");
            Response respWrong = loginTgtWrong.request(MediaType.APPLICATION_JSON).get();
            String strLrWrong = (String) respWrong.readEntity(String.class);
            try {
                LoginResponse lrWrong = mapper.readValue(strLrWrong, LoginResponse.class);
                Long idClienteWrong = lrWrong.getIdCliente();
                assertNotEquals(idClienteWrong, idClienteCorrecto);
            } catch (Exception e) {
                try {
                    mapper.readValue(strLrWrong, ExceptionResponse.class);
                    assertTrue(true);
                } catch (JsonParseException e1) {
                    System.err.println("Error JsonParseException: " + e1.getMessage());
                    e1.printStackTrace();
                } catch (JsonMappingException e1) {
                    System.err.println("Error JsonMappingException: " + e1.getMessage());
                    e1.printStackTrace();
                } catch (IOException e1) {
                    System.err.println("Error IOException: " + e1.getMessage());
                    e1.printStackTrace();
                }
            }
    
        }
    
        private WebTarget buildClientWs(){
            Client client = ClientBuilder.newClient();
            return client.target("http://localhost:9090").path("myWebApp").path("resources").path("Auth");
        }
    
    }
    
  2. The other approach would be executing directly web service methods directly, without the need of an application container. I'm not sure about this, as I wouldn't be testing the Web Service itself.

Any thoughts about this?

Thank you in advance

Upvotes: 1

Views: 866

Answers (4)

keyoxy
keyoxy

Reputation: 4581

I think you are on the right track using an embedded web server to run the service. True, you haven't verified that the packaging into a WAR/docker-image/whatever is correct, but testing must be done in levels and doing functional testing on the web service API level gives you valuable feedback with quick turnaround.

As of tools, I would recommend keeping it simple. Use JUnit + http-matchers (https://github.com/valid4j/http-matchers)

Upvotes: 0

Stanislav Bashkyrtsev
Stanislav Bashkyrtsev

Reputation: 15308

There are multiple options that are not mutually-exclusive:

  • Component Tests. It's possible to write tests without HTTP traffic. Either by directly invoking the service methods or by using an in-memory provider of Jersey (other frameworks usually come with their own capabilities like Spring MVC that comes with MockMVC):

    org.glassfish.jersey.test-framework.providers:jersey-test-framework-provider-inmemory

  • System Tests. This would require an actual HTTP request to be invoked. De-facto standard lib in Java world is RestAssured - it's nice and framework-agnostic (though has some special support for Spring MVC too). You would have to run the app some how - either by starting an in-memory Jetty/Tomcat or by deploying the app separately with external scripts. Also you could try Arquillian which is created specifically to deploy apps for the tests (haven't tried it though).

It's usually a good idea to check most of the logic with Unit and Component tests leaving some coarse-grained checks to System Tests. You don't want to have many system tests since they would be much slower to run and harder to maintain.

Upvotes: 0

crazyGuy
crazyGuy

Reputation: 337

If you are using Jersey, then you can use the built-in test frameworks.

See https://jersey.java.net/documentation/latest/test-framework.html

Upvotes: 2

Boola
Boola

Reputation: 358

If you are trying to write Unit tests then just create mock objects and test your methods only.

But if your requirement is testing webservice as a whole, then you can do integration testing or use Postman plugin.

Upvotes: 2

Related Questions