28

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?

Ion Morozan
  • 783
  • 1
  • 9
  • 13
  • 1
    Where in the above code do you send a JSON string to the server? Why do you even need Mockito for such tests? – JB Nizet May 27 '12 at 15:55
  • 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. – Ion Morozan May 27 '12 at 15:58
  • 1
    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. – JB Nizet May 27 '12 at 16:01
  • 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. – Ion Morozan May 27 '12 at 16:04
  • 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. – JB Nizet May 27 '12 at 16:08
  • 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. – Ion Morozan May 27 '12 at 16:14
  • 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. – JB Nizet May 27 '12 at 16:21
  • 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. – Ion Morozan May 27 '12 at 16:25
  • I want to suggest WireMock. It's a library for stubbing and mocking web services. http://wiremock.org/index.html – Lilylakshi Mar 03 '16 at 06:15

5 Answers5

26

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
}
Maciej Kowalski
  • 25,605
  • 12
  • 54
  • 63
JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255
  • 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`. – Ion Morozan May 27 '12 at 17:13
  • 1
    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. – JB Nizet May 27 '12 at 17:17
3

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.

darrengorman
  • 12,952
  • 2
  • 23
  • 24
  • 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. – Dawood ibn Kareem May 28 '12 at 06:35
  • @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. – darrengorman May 28 '12 at 13:14
2

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.

Dave Newton
  • 158,873
  • 26
  • 254
  • 302
  • 1
    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. – Ion Morozan May 27 '12 at 16:19
  • @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. – Dave Newton May 27 '12 at 16:23
2

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)));
chrki
  • 6,143
  • 6
  • 35
  • 55
0

I agree this is not unit testing but integration test, anyway you'd rather take a look at the jersey and embedded grizzly server tests. To summ it up, this code starts the grizzly server (which could start the datbase as well) at localhost:8888, then setup client's jersey's client and sents a POST request which response should be tested. This is an integration since you're testing both server and database, you may use mockito to emulate the database however, but it depends how tied your server and database are.

(test using jersey 1.11 and grizzly 2.2)

    @BeforeClass
    public static void setUpClass() throws Exception {
        // starts grizzly
        Starter.start_grizzly(true);
        Thread.sleep(4000);
    }

    @Before
    public void setUp() throws Exception {
        client = new Client();
        webResource = client.resource("http://localhost:8888");
    }   

    @Test
    public void testPostSchemaDatabase() throws Exception {
        {
            String DatabaseSchema = "{ database_schema : {...}}";
            logger.info("REST client configured to send: "  + DatabaseSchema);
            ClientResponse response =  
                    webResource
                             .path("/initialize")
                             .type("application/json")
                             .post(ClientResponse.class, DatabaseSchema);
            //wait for the server to process
            Thread.sleep(2000);
            assertEquals(response.getStatus(), 204);    
            //test the response
        }       
    }

    @After
    public void after() throws JSONException
    {
            //probably you want to delete the schema at database and stop the server

}
user311174
  • 1,738
  • 1
  • 18
  • 17