Thursday, February 17, 2011

Is it harder to Unit Test in Grails than Java?. Or am i doing it wrong?







I want to develop a Service (A class method) that does the following. Receives some text, calls (POST to) some HTTP URL with that text as body, and retrieve the value of a cookie (SESSION_ID cookie) from the response.

If i were solving this in Java i would start with some test like this (This code doesn't compile I just wrote it in notepad):

  1. private CommunicationService testObj;
  2. private static String REQUEST=”ANY_REQUEST_WILL_DO”
  3. private static String URL_CONST=”http://www.google.com”
  4. private static String SESSION_ID=”aassdd”
  5. private Configuration conf;
  6. private CookieValueExtractor cookieValueExtractor;
  7. private HttpClient httpClient;
  8. private HttpResponse response;
  9.  
  10. @Before
  11. public void setup(){
  12.     conf=new MockConfiguration();
  13.     cookieValueExtractor=new MockCookieValueExtractor();
  14.     httpClient=new MockHttpClient();
  15.     response=new MockHttpResponse();
  16.     testObj.setConfiguration(conf);
  17.     testObj.setCookieValueExtractor(cookieValueExtractor);
  18.     testObj.setHttpClient(httpClient);
  19. }
  20.  
  21. @Test
  22. public void testSendRequestToHttpAndReturnSessionId(){
  23.     Capture<HttpPost> httpPost=new Capture<HttpPost>();
  24.     expect(conf.getServerUrl()).andReturn(URL_CONST);
  25.     expect(httpClient.execute(capture(httpPost))).andReturn(response);
  26.     expect(cookieValueExtractor.extractCookieByName(response,“SESSION_ID”)).andReturn(SESSION_ID)
  27.     String cookieValue = testObj.sendRequest(REQUEST);
  28.     assertEquals(URL_CONST,httpPost.getValue().getUri().toString());
  29.     assertEquals(SESSION_ID,cookieValue);
  30. }




Taking all setup code out, and focusing on the test method, we can see what we expect our method will do.

1. It will retrieve a URL for a Configuration facility
2. It will use HttpClient to send a Post to that URL
3. It will call a CookieExtractor facility to extract the cookie value we are interested in.
4 It will return that cookie value to the caller.


It is pretty easy to understand.

I now have the same requirement in a Grails project. Of course i want to use the power of Groovy and Grails, so i check the HttpBuilder library out, read a little bit about it and start to work.

The httpBuilder basically works by passing two parameters. A Map, with all the options (Including body, Path, etc) and a Closure that will get the response from the invocation.

Also i need to obtain some values like the URL from grails configuration files.

At the end, going to my implementation class and my test many times while developing(which i usually don’t need to in Java) i end with the following.


  1. void testMakeRequest() {
  2.        configureConfigurationHolder()
  3.        moneybookerService=new MoneybookerService()
  4.        def httpBuilderMock=new MockFor(HTTPBuilder.class)
  5.        httpBuilderMock.demand.post{
  6.            a,b ->
  7.            assertEquals("test_payment.pl",a["path"])
  8.            
  9.            def resp=new DummyResponseWithCookie()
  10.            resp.init()
  11.            def sessionId=b.call(resp)
  12.            
  13.            assertEquals("123123", sessionId)
  14.            
  15.        }
  16.        def mockService=httpBuilderMock.proxyInstance()
  17.        moneybookerService.httpBuilder=mockService
  18.        moneybookerService.makeRequest()    
  19.        httpBuilderMock.verify mockService
  20.     }
  21.  
  22. private void configureConfigurationHolder(){
  23.        def expando=new Expando()
  24.        expando.url=""
  25.        expando.path="test_payment.pl"
  26.        ConfigurationHolder.config=["moneybooker":expando]
  27.    }
  28.    
  29.    class DummyResponseWithCookie{
  30.        def headers
  31.        def init(){
  32.            def expando = new Expando()
  33.            expando.name="SESSION_ID"
  34.            expando.value="123123"
  35.            def outerExpando=new Expando()
  36.            outerExpando.elements=[expando]
  37.            headers=["Set-Cookie":outerExpando]
  38.        }

A lot of work. The main problems that I find are

First and most important. How do i Test closures?. In my case, i had to mock a response object (DummyResponseWithCookie) and because i knew what i wanted the closure to do, call the closure with that mock. But this doesn’t look good. I’m calling the closure in the demand of the mock of httpBuilder. So this test is testing more than what it should. But i don’t know how to test the closure on its own.

Second, the way to mock the configuration holder. Just setting static variables doesn’t seem very right.

Third. This is probably lack of practice but It took me longer to write this test, and was really difficult to think exactly what i wanted to achieve in my service, what needed to be mocked and in general how to approach it in a TDD way.

I really like Groovy, and Grails. I just don’t know how to approach TDD and Unit testing in general in a way that feels as comfortable as with Java.