Well, yes.. that was pretty much the problem I was combating with 🙂
It was to write an Integration test for a Grails Service. The Service talks to a REST end-point. I need to test the service behavior against the different types of responses returned from this End Point.
class MyService {
def endPointAPI
def process (def inputDataMap) {
def parametersToEndPoint = manipulate(inputDataMap)
def endPointResult = endPointAPI.send (parametersToEndPoint)
if (endPointResult == SOME_VALUE1) {
return serviceLogicForSomeValue1 (parametersToEndPoint, endPointResult)
} else if (endPointResult == SOME_VALUE2) {
return serviceLogicForSomeValue2 (parametersToEndPoint, endPointResult)
} else {
return false
}
}
}
I have three different test cases for this Service class. The first for a value SOME_VALUE1 returned by End Point. I know that one such test would be good enough to cover the serviceLogicForSomeValue1 of the Service. Similary, the second should assume SOME_VALUE2 from End Point that helps to testserviceLogicForSomeValue1 of Service. Now, the third is to test the logic that gets executed for any value returned by the End Point other than SOME_VALUE1 and SOME_VALUE2.
So clearly, the first two tests do not require the flow to run the whole End Point logic, as it can assume the End Point values by stubing it through Groovy meta-programming. And, the third do not require any stubbing at all, since the test is OK with any value returned from the End Point. With this, there is an additional benefit of getting the original flow tested from the Service through to End Point.
So, here it goes the test cases.
class MyServiceTests extends BaseIntegrationTests {
def myService
void testServiceLogicForSomeValueOne () {
def inputDataMap = prepareInputDataMap ()
myService.endPointAPI.metaClass.send = { def dataToSent ->
return SOME_VALUE1
}
def processResult = myService.process (inputDataMap)
assertEquals (expectedProcessResultFromServiceLogicForSomeValue1, processResult)
}
void testServiceLogicForSomeValueTwo () {
def processInputArguments = prepareInputDataMap ()
myService.endPointAPI.metaClass.send = { def dataToSent ->
return SOME_VALUE2
}
def processResult = myService.process (inputDataMap)
assertEquals (expectedProcessResultFromServiceLogicForSomeValue2, processResult)
}
void testServiceLogicIfNotSomeValueOneOrSomeValueTwo () {
def processInputArguments = prepareInputDataMap ()
def processResult = myService.process (inputDataMap)
assertEquals (false, processResult)
}
}
Now, run the test using “grails test-app :integration MyService”, and inspects the result. You would find that the first two test cases did run fantastically for you. But have you got confused with what you see with the result of the third?
In the third, you would see that myService.process is bringing in expectedProcessResultFromServiceLogicForSomeValue2( or expectedProcessResultFromServiceLogicForSomeValue1 based on which got modified last). Oh, why ?
Of course.. if the third still gets expectedProcessResultFromServiceLogicForSomeValue2 then it still keeps the reference to the stub injected by the second. But why?
ok then, let me ask you one thing.. why did you expect that it won’t retain the changes in the first place? Did you not presume for some reason that every test execution method starts with a clean service object.
Well, this is true for a Unit Test Case. GrailsUnitTestCase and GrailsUnitTestMixin classes provide in-built support for this through its registerMetaClass and tearDown methods. But not for Integration Test Framework !!!
Grails retains a singleton service class for one full run of the integration test class across all of its test methods – so are the metaclass modifications we applied to. Probably, what we may have got confused with the Grails feature of Transaction Demarcation ensured for every integration test method that reverts any database modification done by each test method.
Now, can I somehow mixin or inject the GrailsUnitTestCase within the Integration Test Execution ? Test framework ensures that only one Test Scope is enabled for one test execution. So mixing-in or injecting won’t be an option, since then Grails would turn on the Unit Testing scope where we would not be having the features like transactional support that an integration test requires.
Ok, then how about this option? Let the test case store the references of to-be-metaprogrammed objects in its setup method, then subsequently let the tearDown method to nullify its metaclass so that the temporary changes will go off. You would have immediately realized the danger involved with this. Nullifying the metaclass would not only the removes the test class metaprogramming, but also any of the metaprogramming implemented the application flow. So, no.. not to go in this track down.
How about mimicking the behaviour of GrailsUnitTestCase within my testing framework? How about invoking those methods of GrailsUnitTestCase programmatically from my integration test case? GrailsUnitTestCase’s registerMetaClass method helps to register any Groovy class that undergoes metaprogramming which helps its tearDown method to revert the state of metaClass to the state before the changes. So theoratically speaking, I can copy those methods and its dependent internal logic to simulate the same. But this would be a very brittle one to go for. I have seen that the metaclass cleanup logic in Groovy 2.1.9 is drastically different from that in Groovy 1.8. So, it is not at all a good practice to copy the Groovy internal logic into our source code which can go broken during the upgrades.
Yes, this idea was driven out of academical curiousity to know how Groovy does all this metaclass cleanup – not a one I really expected to come out as a solution as such. At the same time, I wanted to ensure that I did not miss out to explore all the possible ways 🙂
Till now, our all thought process was in the direction of reverting the metaprogramming changes. Each of our solutions were rooted around it. How about taking a different track? Like, I don’t care what happened to the metaprogrammed instance – but make a new instance available to every test method – how about it?
But that would mean that I cannot depend on the singleton service spring bean created by Grails. If I am going to take care of the creation of that object with all of its dependencies, there wouldn’t be any thing worse than my day-to-day test case development cycle. Life is going to be more and more difficult as I go through more and more test classes.
World is with full of solutions 🙂 It was my team mate who took me through this line of thinking with an idea proposed by an article at http://naleid.com/blog/2011/03/07/creating-new-instances-of-spring-singleton-beans-with-grails-beanbuilder.
This article explains how can we create a grails service bean object using the Grails bean builder support. That would mean that, I don’t have to worry about its dependencies – which is already known to the Grails bean builder API.
At last.. gone are those dark clouds … and its bright shiny day again …. 🙂 Of course that poetic one is a copy-paste 🙂 But I don’t care, I felt the very same when I reached here 🙂
Still, there is one thing I did not get answer for. Why Integration Test Execution is treated differently from Unit Test Execution when it came to the service object life cycle? Anyway, not now – may be for another time 🙂