Grails Spock testing for a specific exception - grails

I'm working with Grails 2.4.5 and struggling to get Grails to delineate different exception types.
Suppose I want to mock the below:
class FooController {
def barService
...
def fooAction() {
try {
barService.someMethod(params)
} catch(e) {
if (e instanceof FooException) { ... }
else if (e instanceof BarException) { ... }
else { ... }
}
}
Given the test below
#TestFor(FooController)
class FooControllerSpec extends Specification {
def setup() { controller.barService = Mock(BarService) }
void "test"() {
given: "a mock dependency"
1* controller.barService.someMethod(_) >> { -> throw FooException('foo') }
when: "the action is requested"
controller.fooAction()
then: "expect the FooException behaviour from the action"
// some behaviour
}
I would expect the FooException within the mock dependency closure to have thrown
however, debugging shows the below instead:
groovy.lang.MissingMethodException: No signature of method: somePackage.FooControllerSpec$_$spock_feature_0_9_closure14.doCall() is applicable for argument types: (java.util.Arrays$ArrayList) values: [[[:]]]
Is this a bug? is there a way to mock different Exceptions in the fashion described above?

You need to specify behaviour in the then block, ie:
void "test"() {
when: "the action is requested"
controller.fooAction()
then: "expect the FooException behaviour from the action"
1 * controller.barService.someMethod(_) >> { -> throw new FooException('foo') }
response.redirectedUrl == "/some/other/path"
}

Related

How to test whether an instance method is called from within another instance method in Grails 3 using Spock

Using Grails 3.2.8 and the Spock framework for testing, given the following controller class:
class SomeController {
def doSomething() {
// do a few things, then:
someOtherMethod()
}
protected void someOtherMethod() {
// do something here, but I don't care
}
}
How can I test the doSomething() method to make sure someOtherMethod() is called exactly once?
This is my attempt that failed:
#TestFor(SomeController)
class SomeControllerSpec extends Specification {
void "Test that someOtherMethod() is called once inside doSomething()"() {
when:
controller.doSomething()
then:
1 * controller.someOtherMethod(_)
}
}
Error message:
Too few invocations for:
1 * controller.someOtherMethod(_) (0 invocations)
Note: Imports have been omitted to focus on the problem at hand
You can't do that as controller is not a mocked object. Instead, you need to use metaclass like this:
#TestFor(SomeController)
class SomeControllerSpec extends Specification {
void "Test that someOtherMethod() is called once inside doSomething()"() {
given:
Integer callsToSomeOtherMethod = 0
controller.metaClass.someOtherMethod = {
callsToSomeOtherMethod++
}
when:
controller.doSomething()
then:
callsToSomeOtherMethod == 1
}
}

Unable to populate java.util.getTimeZone in Grails 3 for spock tests in a Taglib

I am stuck somewhere in Grails 3 spock Testing for Taglibs.
I want to test a closure of taglib, which looks like:
ATagLib.groovy:
Closure a = {attrs ->
java.util.TimeZone utc = java.util.TimeZone.getTimeZone(FraudnetConstants.TIMEZONE_UTC)
if (attrs.somevar) {
out << "${g.message(code: 'some.code')} ${g.formatDate([date: attrs.date, format: "h:mma", timeZone: utc])}"
} else if (attrs.freq == SomeConstants.VAL || attrs.freq == SomeConstants.VAL) {
out << g.formatDate([date: attrs.date, format: "E '#' h:mma", timeZone: utc])
} else {
out << "${g.message(code: 'some.other.code')} ${g.formatDate([date: attrs.date, format: "h:mma", timeZone: utc])}"
}
}
My ATagLibSpec.groovy looks like:
#TestFor(ATagLib)
class ATagLibSpec {
def setup() {
java.util.TimeZone.metaClass.'static'.getTimeZone = {a -> return null }
}
void 'testmethod'() {
expect:
taglib.a()
}
}
The exception i am getting while running testcases is:
java.lang.NullPointerException: Cannot invoke method getTimeZone() on null object at org.grails.plugins.web.taglib.FormatTagLib$_closure2.doCall(FormatTagLib.groovy:170) at groovy.lang.Closure.call(Closure.java:414)
at org.grails.taglib.TagOutput.captureTagOutput(TagOutput.java:64)
at org.grails.taglib.TagLibraryMetaUtils.methodMissingForTagLib(TagLibraryMetaUtils.groovy:138)
at org.grails.taglib.NamespacedTagDispatcher.methodMissing(NamespacedTagDispatcher.groovy:59)
Can someone point here, what's wrong with the above way of prepopulating getTimeZone.
instead of using metaprogramming, it is best to inject a TimezoneFactoryService into your taglib. Having to metaprogram in a test is in my experience an indication of code smell: your code uses a static method to instantiate an object, instead of dependency injection.
Your code could look like this:
Closure a = {attrs->
java.util.TimeZone utc = timezoneFactoryService.getTimeZone(FraudnetConstants.TIMEZONE_UTC)
}
This will allow you to mock your factory service in your spec in a way more convenient way, by using a regular Spock Mock.
#TestFor(ATagLib)
class ATagLibSpec {
def setup() {
taglib.timezoneFactoryService=Stub(TimezoneFactoryService) {
getTimeZone(_) >> null
}
}
void 'testmethod'() {
expect:
taglib.a()
}
}
If you still want to use metaprogramming, be aware that the signature of the method has to match fully (also the parameter types), so:
java.util.TimeZone.metaClass.'static'.getTimeZone = {a -> return null }
java.util.TimeZone.getTimeZone("America/Los_Angeles")
This code will get the timezone for America/Los Angeles, but
java.util.TimeZone.metaClass.'static'.getTimeZone = {String a -> return null }
java.util.TimeZone.getTimeZone("America/Los_Angeles")
this one will return null as we have modified the method properly via metaprogramming.
Be also aware that you have to use the injected variable tagLib not taglib.

MissingMethodException: No signature of method error after replacing method using groovy metaclass?

I have a simple domain
class Cat {
String name
static constraints = {
}
}
The controller is as follows:
class CatController {
def index() { }
def say(){
def cat = new Cat(name: "cc")
cat.save()
render "done"
}
}
and i have a test integration test as follows:
class CatTests extends GroovyTestCase{
CatController controller = new CatController()
#Test
void testSomething() {
Cat.metaClass.save = {
throw new Exception("Asdasd")
}
shouldFail(Exception){
controller.say()
}
GroovySystem.metaClassRegistry.removeMetaClass(Cat.class)
}
#Test
void testSomething2() {
def before_cat_count = Cat.count()
controller.say()
def after_cat_count = Cat.count()
assertEquals after_cat_count - before_cat_count, 1
}
}
I get the following error when running the test.
Failure: testSomething2(cat.CatTests)
| groovy.lang.MissingMethodException: No signature of method: cat.Cat.count() is applicable for argument types: () values: []
Possible solutions: count(), ident(), print(java.lang.Object), print(java.io.PrintWriter), getCount(), wait()
at cat.CatTests.testSomething2(CatTests.groovy:37)
| Completed 2 integration tests, 1 failed in 192ms
Now, my doubt and question is why is it not finding the count method on this line
def before_cat_count = Cat.count()
of testSomething2() method.
I have already removed the meta class at the end of method testSomething. So, i am wondering why is count() method not being found. I appreciate any help! Thanks!
Perhaps simply
GroovySystem.metaClassRegistry.removeMetaClass(Cat)
simply Cat, not Cat.class
Sourced from the web, I use a method for this type of cleanup.
def revokeMetaClassChanges(Class type, def instance = null) {
GroovySystem.metaClassRegistry.removeMetaClass(type)
if(instance) {
instance.metaClass = null
}
}
With invocations like:
cleanup:
revokeMetaClassChanges(FirstService, firstServiceInstance)
revokeMetaClassChanges(SecondService, null)
with much success.

assertEquals controller.action() return null....!

class ProjectDashBoardController {
/*
* This method is used to display project implementation's overview.
*/
def check() {
render "Hello"
}
and this is my integration test
package com.spock
import grails.plugin.spock.IntegrationSpec
class ProjectDashBoardControllerIntegrationSpec extends IntegrationSpec {
ProjectDashBoardController controller = new ProjectDashBoardController()
def cleanup() {
}
void "test check action"() {
when:
controller.check()
then:
controller.check()
assertEquals "/dashboard/index", controller.response.redirectedUrl
}
}
as per the, then condition the test should fail since action render but instead it showing such error
No signature of method: com.itxchg.ProjectDashBoardControllerIntegrationSpec.assertEquals() is applicable for argument types: (java.lang.String, null) values: [/dashboard/index, null]
Spock doesn't have an assertEquals method, you should use regular Groovy power asserts, although assert is implicit in then block when spock is used:
assert "/dashboard/index" == controller.response.redirectedUrl
In your case though you do have have a redirect scenario to test. What actually should be tested is that the response consists the rendered text:
void "test check action"() {
when:
controller.check()
then:
// Note there is no need to explicitly specify assert in then block
// Assertion is implicit
controller.response.text == "Hello"
}

Is it OK to store additional objects in an Exception?

I needed to display some validation messages received from the service each in its proper place, and I solved it putting the messages in an exception:
class InvalidInputException extends RuntimeException {
def errors
InvalidInputException(String s) {
super(s)
}
InvalidInputException(String s, errors) {
super(s)
this.errors = errors
}
}
That way, I could throw the exception sending the errors:
if (errors) {
throw new InvalidInputException("There were some errors", errors)
}
.. and then I deal with the errors later in the controller, after catching the exception:
...
catch (InvalidInputException e) {
if (e.errors) {
// set them up to display appropriately
}
// render the view
}
Now, I've read somewhere that Groovy's exceptions can cost too much, so... is this too bad?
What problems may I encounter putting additional data in an Exception?
It's much easier than fiddling with returned error messages, and the code is much shorter.
If you are concerned about the performance of exceptions in Java, I suggest you to look at this other question.
If you not create a exception, the other possibility is to make your service return an object that represents the result of this flow. Something like:
class MyServiceResult {
List<String> errorCodes = [] //codes for i18n
void addErrorCode(String errorCode) {
errorCodes << errorCode //add error to list
}
boolean isValid() {
return (!(errorCodes.size() > 0)) //if we have errors then isn't valid.
}
List<String> getErrorCodes() {
return errorCodes.asImmutable()
}
}
And just use it in your service method
class MyService {
MyServiceResult someMethod() {
MyServiceResult result = new MyServiceResult()
...
result.addErrorCode('some.key.here')
...
return result
}
}
class MyController {
def myService
def action() {
MyServiceResult result = myService.someMethod()
if(!result.isValid()) {
//handle errors
}
}
}
But it's important to say that it can be 2x slower than creating an exception. You can check all details in this post.

Resources