How to use mockito for testing a REST service?

91,554

Solution 1

OK. So, the contract of the method is the following: Parse the input string as JSON, and send back BAD_REQUEST if it's invalid. If it's valid, create an entity in the datastore with various properties (you know them), and send back OK.

And you need to verify that this contract is fulfilled by the method.

Where does Mockito help here? Well, if you test this method without Mockito, you need a real DataStoreService, and you need to verify that the entity has been created correctly in this real DataStoreService. This is where your test is not a unit test anymore, and this is also where it's too complex to test, too long, and too hard to run because it needs a complex environment.

Mockito can help by mocking the dependency on the DataStoreService: you can create a mock of DataStoreService, and verify that this mock is indeed called with the appropriate entity argument when you call your initialize() method in your test.

To do that, you need to be able to inject the DataStoreService into your object under test. It can be as easy as refactoring your object in the following way:

public class MyRestService {
    private DataStoreService dataStoreService;

    // constructor used on the server
    public MyRestService() {
        this.dataStoreService = DatastoreServiceFactory.getDatastoreService();
    }

    // constructor used by the unit tests
    public MyRestService(DataStoreService dataStoreService) {
        this.dataStoreService = dataStoreService;
    }

    public Response initialize(String DatabaseSchema) {
         ...
         // use this.dataStoreService instead of datastore
    }
}

And now in your test method, you can do:

@Test
public void testInitializeWithGoodInput() {
    DataStoreService mockDataStoreService = mock(DataStoreService.class);
    MyRestService service = new MyRestService(mockDataStoreService);
    String goodInput = "...";
    Response response = service.initialize(goodInput);
    assertEquals(Response.Status.OK, response.getStatus());

    ArgumentCaptor<Entity> argument = ArgumentCaptor.forClass(Entity.class);
    verify(mock).put(argument.capture());
    assertEquals("the correct kind", argument.getValue().getKind());
    // ... other assertions
}

Solution 2

What you're talking about sounds more like integration testing and Mockito (or any other mocking frameworks) will not be of much use to you.

If you want to unit test code you've written, Mockito is certainly a useful tool.

I suggest you read more about mocking/unit testing and which circumstances it should be used in.

Solution 3

Best method is to use wiremock Add the following dependencies com.github.tomakehurst wiremock 2.4.1 org.igniterealtime.smack smack-core 4.0.6

Define and use the wiremock as shown below

@Rule
public WireMockRule wireMockRule = new WireMockRule(8089);

String response ="Hello world";
StubMapping responseValid = stubFor(get(urlEqualTo(url)).withHeader("Content-Type", equalTo("application/json"))
        .willReturn(aResponse().withStatus(200)
                .withHeader("Content-Type", "application/json").withBody(response)));

Solution 4

Mockito is (generally) for testing portions of code; for example if you were consuming your REST service, but didn't want to do a full-stack test, you'd mock the service that connected to the REST service, allowing you to precisely, and consistently, test specific behavior.

To test internal portions of the REST service (e.g., a specific service method) without hitting a database, you'd mock the DB subsystem, allowing testing of service internals only, without involving the DB. This testing belongs in the REST service module, not the client side.

To test the REST service itself you'd use an actual client library, creating a full-stack integration test. Mockito could be used here to mock portions of the client unrelated to REST service consumption.

Share:
91,554

Related videos on Youtube

Ion Morozan
Author by

Ion Morozan

Updated on January 13, 2020

Comments

  • Ion Morozan
    Ion Morozan over 4 years

    I am very new in Java Unit Testing and I heard that Mockito framework is really good for testing purposes.

    I have developed a REST Server (CRUD methods) and now I want to test it, but I don't know how?

    Even more I don't know how this testing procedure should begin. My server should work on localhost and then make calls on that url(e.g. localhost:8888)?

    Here is what I tried so far, but I'm pretty sure that this isn't the right way.

        @Test
        public void testInitialize() {
            RESTfulGeneric rest = mock(RESTfulGeneric.class);
    
            ResponseBuilder builder = Response.status(Response.Status.OK);
    
            builder = Response.status(Response.Status.OK).entity(
                    "Your schema was succesfully created!");
    
            when(rest.initialize(DatabaseSchema)).thenReturn(builder.build());
    
            String result = rest.initialize(DatabaseSchema).getEntity().toString();
    
            System.out.println("Here: " + result);
    
            assertEquals("Your schema was succesfully created!", result);
    
        }
    

    Here is the code for initialize method.

        @POST
        @Produces(MediaType.APPLICATION_JSON)
        @Path("/initialize")
        public Response initialize(String DatabaseSchema) {
    
            /** Set the LogLevel to Info, severe, warning and info will be written */
            LOGGER.setLevel(Level.INFO);
    
            ResponseBuilder builder = Response.status(Response.Status.OK);
    
            LOGGER.info("POST/initialize - Initialize the " + user.getUserEmail()
                    + " namespace with a database schema.");
    
            /** Get a handle on the datastore itself */
            DatastoreService datastore = DatastoreServiceFactory
                    .getDatastoreService();
    
    
            datastore.put(dbSchema);
    
            builder = Response.status(Response.Status.OK).entity(
                    "Your schema was succesfully created!");
            /** Send response */
            return builder.build();
        }
    

    In this test case I want to send a Json string to the server(POST). If everything went well then the server should reply with "Your schema was succesfully created!".

    Can someone please help me?

    • JB Nizet
      JB Nizet almost 12 years
      Where in the above code do you send a JSON string to the server? Why do you even need Mockito for such tests?
    • Ion Morozan
      Ion Morozan almost 12 years
      I don't know if I need Mockito for those kind of tests, as I said I m trying to understand how to test a REST service. The JSON file is sending when I'm calling this method : rest.initialize(DatabaseSchema) , DatabaseSchema is a JSON file.
    • JB Nizet
      JB Nizet almost 12 years
      No, it doesn't. rest is a mock. It's an object that answers what you tell it to answer. It's not your rest server. You're testing Mockito instead of testing your server. You don't need Mockito for such tests.
    • Ion Morozan
      Ion Morozan almost 12 years
      Thank you! Can you please guide me, how should I do? I want to test my CRUD methods. I want unit tests, to test every rest method on my server.
    • JB Nizet
      JB Nizet almost 12 years
      Either you do integration tests and actually send JSON to your server and analyze the answer you get to make sure they are appropriate, or you do unit tests to test methods of your classes used at server-side. Mockito can be useful for the latter. But without seeing your code, it's impossible to tell you how to test it, other than pointing you to the documentation of Mockito.
    • Ion Morozan
      Ion Morozan almost 12 years
      When you said to do integration tests and send JSON to server, you meant to use HttpClient and actually send request to the server API url? But those kind of tests are not unit tests, because I am testing individual methods! I think I'm confusing or it's not clear for me.
    • JB Nizet
      JB Nizet almost 12 years
      Yes, that's what I meant with integration tests. That's also what you asked for: In this test case I want to send a Json string to the server(POST). If you want to test an individual method of an object, then fine, but it's not what you asked for. And without knowing what this method is supposed to do, what is its name, arguments and dependencies, it's not possible to help.
    • Ion Morozan
      Ion Morozan almost 12 years
      First of all thank you so much for helping me. In this case method initialize on the server side, receive a JSON file like this: {"kind":"Note", "property":["id","name","note","date"]} and then after parsing the JSON and extracting properties creates a database with this schema, where kind is the name of DB and properties are the fields(columns). I have updated the question with the code of this method. Then after initialization I want to test the Add, Update, Delete, Get method.
    • Lilylakshi
      Lilylakshi about 8 years
      I want to suggest WireMock. It's a library for stubbing and mocking web services. wiremock.org/index.html
  • Ion Morozan
    Ion Morozan almost 12 years
    Thank you for the response. Actually what I need is to test a REST service that I have developed, so I want to test individual methods, but I don't know how. For example as I said in my question I want to send a JSON to server and verify the response. I don't know where should I make the request, on the localhost or where I have deployed the API. I'am using GAE as a backend.
  • Dave Newton
    Dave Newton almost 12 years
    @IonMorozan Not sure I understand the question--you can test locally, you can test deployed, it depends on which you want to test. The request would likely be made locally, regardless of the endpoint.
  • Ion Morozan
    Ion Morozan almost 12 years
    Thank you! I have a question: here was supposed to be verify(mockDataStoreService).put(argument.capture()); ? Even if it did after I changed to mockDataStoreService I get an error The method verify(DatastoreService) is undefined for the type MyRestService.
  • JB Nizet
    JB Nizet almost 12 years
    The test method should be in a unit test class, not in the class that you test. And EasyMock is typically used with a import static org.mockito.Mockito.*. verify is a static method of the Mockito class.
  • Dawood ibn Kareem
    Dawood ibn Kareem almost 12 years
    I can't agree with this answer at all. I have profitably used Mockito in loads of integration tests. Often, there are integration tests in which whole subsystems are irrelevant and can reasonably be mocked. Mockito (or some other mocking framework) is generally useful whenever you want to test some part of a system, without interactions from other parts cloudying what's happening in your test. This might be a unit test, a volume test, or an integration test.
  • darrengorman
    darrengorman almost 12 years
    @DavidWallace Agreed. My wording was misleading, I meant that with what the OP was describing (deploying his app and firing REST requests at it), mocking would not be of much use.