I have a controller like this:
class NotificationApiController {
def countService
def getCount() {
def results = countService.getCount()
render results as JSON
}
}
And the controller test like this:
Closure doWithSpring() {{ ->
countService(CountService)
}}
CountService countService
def setup() {
}
def cleanup() {
}
void "test getCount method"() {
given:
def countService = Mock(CountService) {
1 * getCount(_) >> [count: 2]
}
when:
def y = controller.getCount()
then:
y == [count: 2]
}
It appears it always calls into the actual CountService injected in Closure doWithSpring(), not my mock countService, but without the definition of Closure doWithSpring()..., I will get this error
Cannot invoke method getCount() on null object
java.lang.NullPointerException: Cannot invoke method getCount() on null object
The documentation on unit testing in 4.0 is really limited and I am not exactly sure how should I do this. I see some samples in Grails 2.3 or 3.3. version, but they all seems not working for me, mostly due to the difference of Spock and Mixin framework I guess. Any suggestions on how to do this?
You have omitted some details that might affect the recommendation but the project at https://github.com/jeffbrown/chrisjiunittest shows 1 way to go about this.
https://github.com/jeffbrown/chrisjiunittest/blob/a59a58e3b6ed6b47e1a8104f3e4d3bdb138abacc/src/test/groovy/chrisjiunittest/NotificationApiControllerSpec.groovy
package chrisjiunittest
import grails.testing.web.controllers.ControllerUnitTest
import spock.lang.Specification
class NotificationApiControllerSpec extends Specification implements ControllerUnitTest<NotificationApiController> {
void "test something"() {
setup:
// whether or not this is the right thing to do
// depends on some other factors, but this is
// an example of one approach...
controller.countService = Mock(CountService) {
getCount() >> [count: 2]
}
when:
controller.getCount()
then:
response.json == [count: 2]
}
}
Another option:
package chrisjiunittest
import grails.testing.web.controllers.ControllerUnitTest
import spock.lang.Specification
class NotificationApiControllerSpec extends Specification implements ControllerUnitTest<NotificationApiController> {
Closure doWithSpring() {
// whether or not this is the right thing to do
// depends on some other factors, but this is
// an example of one approach...
{ ->
countService MockCountService
}
}
void "test something"() {
when:
controller.getCount()
then:
response.json == [count: 2]
}
}
class MockCountService {
Map getCount() {
[count: 2]
}
}
Related
Spock does not detect doTip method invocation
(I need shared for some "where" blocks.)
Used latest groovy and spock.
Why this code is wrong?
How fix it?
import spock.lang.Shared
import spock.lang.Specification
class Test extends Specification {
def controller
#Shared
String g = ""
#Shared
def tip = Mock(Tip)
def "test"() {
controller = new TController(tip: tip)
when:
controller.transform(g)
then:
1 * tip.doTip(_)
}
}
class Tip {
def doTip(String f) {}
}
class TController {
Tip tip
def transform(String g) {
tip.doTip(g)
}
}
Use setup() to create the mock as shown below:
#Grab(group='org.spockframework', module='spock-core', version='1.0-groovy-2.4')
import spock.lang.*
class Test extends Specification {
def controller
#Shared String g = ""
#Shared tip
def setup() {
tip = Mock(Tip)
}
def "test"() {
given:
controller = new TController(tip: tip)
when:
controller.transform(g)
then:
1 * tip.doTip(_)
}
}
class Tip {
def doTip(String f) {}
}
class TController {
Tip tip
def transform(String g) {
tip.doTip(g)
}
}
Result
JUnit 4 Runner, Tests: 1, Failures: 0, Time: 78
Given an extended example from the Grails documentation (http://grails.org/doc/latest/guide/async.html#asyncRequests):
import static grails.async.Promises.*
class myController {
def myServiceWithLongRunningMethod
def index() {
tasks otherValue: {
// do hard work
myServiceWithLongRunningMethod.doSomething()
}
}
}
How do I create a Spock unit test for this?
import static grails.async.Promises.*
import org.grails.async.factory.*
import grails.async.*
import grails.test.mixin.TestFor
import spock.lang.Specification
#TestFor(MyController)
class MyControllerSpec extends Specification {
def mockMyServiceWithLongRunningMethod = Mock(MyServiceWithLongRunningMethod)
def setup() {
// not sure if I need this next line
// Grails docs suggests making Promises synchronous for testing
Promises.promiseFactory = new SynchronousPromiseFactory()
controller.myServiceWithLongRunningMethod = mockMyServiceWithLongRunningMethod
}
def cleanup() {
}
void "index calls myServiceWithLongRunningMethod.doSomething()"() {
when: 'index is called'
controller.index()
then: 'myServiceWithLongRunningMethod.doSomething() is called'
1 * mockMyServiceWithLongRunningMethod.doSomething()
}
}
I get the following error:
Failure: index calls myServiceWithLongRunningMethod.doSomething()(MyControllerSpec)
| groovy.lang.MissingPropertyException: No such property: tasks for class: MyLoadingController
I'm not sure what the tests should look like - it's clear that I need to do something to enable the test class to understand the tasks method provided by Promise.
Given the following, how do I mock processMessage() using Spock, so that I can check that processBulkMessage() calls processMessage() n times, where n is the number of messages within a BulkMessage?
class BulkMessage {
List messages
}
class MyService {
def processBulkMessage(BulkMessage msg) {
msg.messages.each {subMsg->
processMessage(subMsg)
}
}
def processMessage(Message message) {
}
}
You can use spies and partial mocks (requires Spock 0.7 or newer).
After creating a spy, you can listen in on the conversation between the caller and the real object underlying the spy:
def subscriber = Spy(SubscriberImpl, constructorArgs: ["Fred"])
subscriber.receive(_) >> "ok"
Sometimes, it is desirable to both execute some code and delegate to the real method:
subscriber.receive(_) >> { String message -> callRealMethod(); message.size() > 3 ? "ok" : "fail" }
In my opinion this is not a well designed solution. Tests and design walk hand in hand - I recommend this talk to investigate it better. If there's a need to check if other method was invoked on an object being under test it seems it should be moved to other object with different responsibility.
Here's how I would do it. I know how visibility works in groovy so mind the comments.
#Grab('org.spockframework:spock-core:0.7-groovy-2.0')
#Grab('cglib:cglib-nodep:3.1')
import spock.lang.*
class MessageServiceSpec extends Specification {
def 'test'() {
given:
def service = new MessageService()
def sender = GroovyMock(MessageSender)
and:
service.sender = sender
when:
service.sendMessages(['1','2','3'])
then:
3 * sender.sendMessage(_)
}
}
class MessageSender { //package access - low level
def sendMessage(String message) {
//whatever
}
}
class MessageService {
MessageSender sender //package access - low level
def sendMessages(Iterable<String> messages) {
messages.each { m -> sender.sendMessage(m) }
}
}
It does not use Spock built-in Mocking API (not sure how to partially mock an object), but this should do the trick:
class FooSpec extends Specification {
void "Test message processing"() {
given: "A Bulk Message"
BulkMessage bulk = new BulkMessage(messages: ['a', 'b', 'c'])
when: "Service is called"
def processMessageCount = 0
MyService.metaClass.processMessage { message -> processMessageCount++ }
def service = new MyService()
service.processBulkMessage(bulk)
then: "Each message is processed separately"
processMessageCount == bulk.messages.size()
}
}
For Java Spring folks testing in Spock:
constructorArgs is the way to go, but use constructor injection. Spy() will not let you set autowired fields directly.
// **Java Spring**
class A {
private ARepository aRepository;
#Autowire
public A(aRepository aRepository){
this.aRepository = aRepository;
}
public String getOne(String id) {
tryStubMe(id) // STUBBED. WILL RETURN "XXX"
...
}
public String tryStubMe(String id) {
return aRepository.findOne(id)
}
public void tryStubVoid(String id) {
aRepository.findOne(id)
}
}
// **Groovy Spock**
class ATest extends Specification {
def 'lets stub that sucker' {
setup:
ARepository aRepository = Mock()
A a = Spy(A, constructorArgs: [aRepository])
when:
a.getOne()
then:
// Stub tryStubMe() on a spy
// Make it return "XXX"
// Verify it was called once
1 * a.tryStubMe("1") >> "XXX"
}
}
Spock - stubbing void method on Spy object
// **Groovy Spock**
class ATest extends Specification {
def 'lets stub that sucker' {
setup:
ARepository aRepository = Mock()
A a = Spy(A, constructorArgs: [aRepository]) {
1 * tryStubVoid(_) >> {}
}
when:
...
then:
...
}
}
Am getting the following error message when testing the controller - see below for code.
How can I correct this?
When I invoke the service method from the controller (run-app) and it works fine.
Exception:
groovy.lang.MissingMethodException: No signature of method:
grails.test.GrailsMock.isOk() is applicable for argument types:
(java.lang.String) values: [H] at ...VControllerSpec.test
something(VControllerSpec.groovy:)
class: VControllerSpec
import grails.test.mixin.TestFor
import spock.lang.Specification
#TestFor(VController)
#Mock(VService)
class VControllerSpec extends Specification {
void "test something"() {
given:
def vServiceMock = mockFor(VService)
vServiceMock.demand.isOk { String yeah -> return true }
controller.vService = vServiceMock.createMock()
when:
def isO = vServiceMock.isOk("H")
then:
isO == true
}
}
class:VService
import grails.transaction.Transactional
#Transactional
class VService {
def isOk = { String yeah ->
def isO = false
return isO
}
}
Thanks,
Steve
Assuming there is an action in VController as:
def myAction() {
vService.isOk('Hello')
}
below test should pass
void 'test service'() {
given:
def vServiceMock = mockFor(FormatService)
vServiceMock.demand.isOk { String yeah -> return true }
controller.vService = vServiceMock.createMock()
when:
def isO = controller.myAction()
then:
isO == true
}
There are few things to optimize here including using a method isOk instead of a closure as best practices.
One is not expected to test a method which is being mocked. When we mock a method, we just assume its implementation is correct and has already been tested (in some other unit test). The purpose of mocking is to limit our focus of testing to limited lines of code (mostly commonly one method), in your case the your controller action. So the above test case could have been written as:
Assuming your action is like this:
def myAction(){
[iso: vServiceMock.isOk()] // assuming isOk returns boolean true
}
void "test myAction"() {
given:
def vServiceMock = mockFor(VService)
vServiceMock.demand.isOk { String yeah -> return true }
controller.vService = vServiceMock.createMock()
when:
def model = controller.myAction()
then:
model.iso //you can skip comparison with true
}
Total 'testing newbie' wanting to test the addJsFile method of my custom TagLib. What am I missing?
TagLib:
import com.company.group.application.helper.Util
...
class MyTagLib {
static namespace = 'mytag'
def util
...
def addJsFile = {
if (util.isSecureRequest(request)) {
out << '<script src="https://domain.com/jsfile.js"></script>'
} else {
out << '<script src="http://domain.com/jsfile.js"></script>'
}
}
}
Test (as far as I could get):
import org.springframework.http.HttpRequest
import com.company.group.application.helper.Util
#TestFor(MyTagLib)
class MyTagLibTests {
def util
...
void testAddJsFileSecure() {
def mockUtil = mockFor(Util)
mockUtil.demand.isSecureRequest() { HttpRequest request -> true }
def jsCall = applyTemplate('<mytag:addJsFile />')
assertEquals('<script src="https://domain.com/jsfile.js"></script>', jsCall)
}
void testAddJsFileNotSecure() {
def mockUtil = mockFor(Util)
mockUtil.demand.isSecureRequest() { HttpRequest request -> false }
def jsCall = applyTemplate('<mytag:addJsFile/>')
assertEquals('<script src="http://domain.com/jsfile.js"></script>', jsCall)
}
}
Util isSecureRequest
boolean isSecureRequest(request) {
return [true or false]
}
Error
org.codehaus.groovy.grails.web.taglib.exceptions.GrailsTagException: Error executing tag <mytag:addJsFile>: Cannot invoke method isSecureRequest() on null object
You need to set the mocked util in tagLib in order to use it.
void testAddJsFileSecure() {
def mockUtilControl = mockFor(Util)
mockUtilControl.demand.isSecureRequest() { HttpRequest request -> true }
//"tagLib" is the default bind object provided
//by the mock api when #TestFor is used
tagLib.util = mockUtilControl.createMock()
//Also note mockFor() returns a mock control
//which on createMock() gives the actual mocked object
def jsCall = applyTemplate('<mytag:addJsFile />')
assertEquals('<script src="https://domain.com/jsfile.js"></script>', jsCall)
//In the end of test you can also verify that the mocked object was called
mockUtilControl.verify()
}
You would not need def util in the test then.