Spock Testing and mocking or stubing database interaction - unit-testing

I have a private controller method that is used by multiple actions to retrieve an object from the database. I can not for the life of me correctly mock/stub the call to the database. The Controller method is:
private Order getSalesOrder(){
def order = Order.get(params.id)
if(!order){
flash.message = (code: 'default.not.found.message', args: [message(code: 'order.label', default: 'Order'), params.id])
redirect action: "list"
return
}
return order
}
The Test method I have at this point is:
def "test getSalesOrder returns Sales Order"(){
given:
params.id >> 3002L
criteriaSetup()
Order testOrder = salesOrders[2]
Order.metaClass.static.get() >> testOrder
when:
def order = controller.getSalesOrder()
then:
1 * Order.get(3002) >> testOrder
//1 * Order.get() >> salesOrders[2]
order == testOrder
}
My results are either a message about too few invocations or I just get a null value back. Both of which cause the test to fail.
I have tried variations of this by using examples found from various blogs or tutorials such as:
Order.metaClass.static.get() >> testOrder
Order.metaClass.methods.get = { return testOrder }
1 * Order.get(3002) >> testOrder
1 * Order.get(params.id) >> testOrder
I'm not sure where to go from here, since I'm new to spock tests, Mocking and Stubing in general. And due to the fact that we are writing test for legacy code it is even more confusing.

You could simply use #Mock([Order]) on top of your test class declaration

I think your failure case of too few invocations is the correct one. I don't think you can check 1 * Order.get(3002). This call will always return 0 invocations, and you would be checking for 1.
You would either have to mock this method with a counter in it to do the count check.

Related

Unable to create a Spy on a constructor in Spock

The declaration I'm looking to create a spy on:
Producer<String, String> producer = new KafkaProducer<String, String>(producerProps)
My attempts to create the spy failed:
def spySvc = GroovySpy(service, global:true)
2 * new service.createKafkaProducer() >> mockedProducer; // fail
2 * new KafkaProducer(_) >> mockedProducer; // fail
2 * new KafkaProducer<String,String>(_) >> mockedProducer; //fail
But when I factor out the Producer creation into its own method createKafkaProducer() and create a spy on the method invocation like so , it works:
2 * service.createKafkaProducer() >> mockedProducer; // success
Any ideas?
( I realize that the code should be refactored to use proper DI, then the problem is trivial and doesn't need a Spy )
I think #rafaelim is correct , KafkaProducer is a statically compiled code and GroovySpies don't work on it .

Groovy Spock File test

I'm about to start learning spock and I'm trying some basic stuff.
I want to check File's: exist() and getText() functionality
So I wrote the following test:
class MyTestSpec extends Specification {
def "test file"() {
given:
def mockFile = Mock(File,constructorArgs :["./doesNotExist.txt"])
mockFile.exists() >> true
mockFile.getText() >> "sampleText"
when:
def res = ""
if(mockFile.exists()) {
res = mockFile.getText()
}
then:
"sampleText" == res
1 * mockFile.exists()
1 * mockFile.getText()
}
}
This fails on:
Too few invocations for:
1 * mockFile.getText() (0 invocations)
Unmatched invocations (ordered by similarity):
None
When I comment the 'verifications' in 'then' block, I get:
java.lang.NullPointerException at
java.io.FileInputStream.(FileInputStream.java:138) at
groovy.util.CharsetToolkit.(CharsetToolkit.java:69) at
MyTestSpec.Test Existing Resource(MyTestSpec.groovy:83)
So my question is: How Exactly I can organize my test? Why does it assume that getText should not be called?
I'm using groovy 2.4 and spock 1.0
The solution will be:
#Grab('org.spockframework:spock-core:0.7-groovy-2.0')
#Grab('cglib:cglib:3.1')
#Grab('org.ow2.asm:asm-all:5.0.3')
import spock.lang.*
class MyTestSpec extends Specification {
def "test file"() {
given:
def mockFile = GroovyMock(File, constructorArgs :["./doesNotExist.txt"])
when:
def res = ""
if(mockFile.exists()) {
res = mockFile.getText()
}
then:
"sampleText" == res
1 * mockFile.exists() >> true
1 * mockFile.getText() >> "sampleText"
}
}
On of the problems is creating a mock. Because of a dynamic nature of groovy some functionality - e.g. getText() method for File class - is added at runtime. It requires mocks to be constructed in a different way. Have a look at spock mock implementation enum and extract:
An implementation specifically targeting Groovy callers. Supports mocking of dynamic methods, constructors, static methods, and "magic" mocking of all objects of a particular type.
The second problem is defining mock behavior and verifying the interactions. When you both mock and stub it must happen in the same interaction (here, in then block), here is he relevant part of the docs.

Verify Spock mock with specified timeout

In Mockito there is option to verify if mock method has been called, and specify timeout for this verification (VerificationWithTimeout), for example:
verify(mock, timeout(200).atLeastOnce()).baz();
It there any equivalent to such functionality in Spock?
I was trying to use PollingConditions to satisfy a similar scenario (which wasn't helpful), but instead found satisfaction in Spock's BlockingVariables. To verify that SomeService.method() is invoked at least once in function ClassBeingTested.method() within a given timeout period:
def "verify an interaction happened at least once within 200ms"(){
given:
def result = new BlockingVariable<Boolean>(0.2) // 200ms
SomeService someServiceMock = Mock()
someServiceMock.method() >> {
result.set(true)
}
ClassBeingTested clazz = new ClassBeingTested(someService: someServiceMock)
when:
clazz.someMethod()
then:
result.get()
}
When the result is set, the blocking condition will be satisfied and result.get() would have to return true for the condition to pass. If it fails to be set within 200ms, the test will fail with a timeout exception.
There is no equivalent in Spock. (PollingConditions can only be used for conditions, not for interactions.) The closest you can get is to add a sleep() statement in the then block:
when:
...
then:
sleep(200)
(1.._) * mock.baz()
Using PollingConditions and a boolean variable, the following example evaluates a function until it satisfies an interaction.
def "test the config watcher to reload games if a new customer directory is added"() {
given:
def conditions = new PollingConditions(initialDelay: 1, timeout: 60, factor: 1.25)
def mockGameConfigLoader = Mock(GameConfigLoader)
def gameConfigWatcher= new GameConfigWatcher(mockGameConfigLoader)
boolean gamesReloaded = false
when:
conditions.eventually {
gameConfigWatcher.listenEvents()
assert gamesReloaded
}
then:
1 * mockGameConfigLoader.loadAllGameConfigs() >> {
gamesReloaded = true
}
0 * _
}
This doesn't do exactly what the question asked, but I found it a bit cleaner that using a variable. If you have other conditions to asynchronously test in addition to the interaction, then you can declare the interactions at mock creation time and then use PollingConditions to test the other conditions. PollingConditions will either fail the test or block until the conditions pass, so that by the time the interaction is tested, the method should have been called:
#MicronautTest
class KernelManagerTest extends Specification {
#Inject
KernelManager kernelManager
//create conditions
PollingConditions conditions = new PollingConditions(timeout: 10, initialDelay: 1)
class Exits {
static createKernel (String[] args) {
System.exit(args[0].toInteger())
}
}
def "handles a system exit from within kernel"() {
given:
// set custom kernel
kernelManager.kernelClass = Exits
// create custom logger
kernelManager.log = Mock(Logger) {
1 * warn("Kernel exited unexpectedly.", _ as UnexpectedExitException)
}
when:
// create new kernel (exit 1)
kernelManager.startNewKernel("1")
then:
conditions.eventually {
kernelManager.kernelInstances.size() == 0
kernelManager.kernelThreads.size() == 0
}
}
}

Grails Criteria not working in JUnit Test

I'm trying to test search method in my grails app but I'm having a null pointer exception. I mocked the domain in my test as follows:
#TestFor(AuditController)
#Mock(Audit)
class AuditControllerTests {
void testSearch() {
populateValidParams(params)
def audit=new Audit(params)
audit.save(failOnError: true)
//Search existing customer
def model = controller.search()
assert model.auditInstanceList.size() == 1
assert model.auditInstanceList.size() == 1
}
}
I got NPE on model.auditInstanceList. Where it shouldn't be null. Here is the code in my controller:
def search = {
def query
def criteria = Audit.createCriteria()
def results
query = {
and{
if(params.customerName){
ilike("customerName", params.customer + '%')
}
if(params.siteName){
ilike("siteName", params.siteName + '%')
}
max:params.max
offset:params.offset
}
}
results = criteria.list(params, query)
render(view:'list', model:[ auditInstanceList: results,auditInstanceTotal:results.totalCount ])
}
What is going on with this?
Return model at the end of search. As in,
def search = {
...
render(view:'list', model:[ auditInstanceList: results, auditInstanceTotal:results.totalCount ])
[auditInstanceList: results, auditInstanceTotal:results.totalCount]
}
When testing a controller action that calls render(), model and view variables are automatically created and populated in your test. By doing def model = controller.search(), you are replacing the magic model with your own, assigning it to the return value of search(). The correct way to do your assertions is:
controller.search()
assert model.auditInstanceList.size() == 1
assert view == '/audit/list'
I don't know why but sometimes you need to remove model as a return value from controller's action. I use both version alternatively in case one of them fails:
// sometimes this one works
def model = controller.search()
assert model
// sometimes this one works
controller.search()
assert model
Edit: I think of two new possibilities why your action doesn't work:
try to change your action from closure and make it a method.
make sure you have no after filter. I've found this bug: http://jira.grails.org/browse/GRAILS-6825

How to test a Grails Service that utilizes a criteria query (with spock)?

I am trying to test a simple service method. That method mainly just returns the results of a criteria query for which I want to test if it returns the one result or not (depending on what is queried for).
The problem is, that I am unaware of how to right the corresponding test correctly. I am trying to accomplish it via spock, but doing the same with any other way of testing also fails.
Can one tell me how to amend the test in order to make it work for the task at hand?
(BTW I'd like to keep it a unit test, if possible.)
The EventService Method
public HashSet<Event> listEventsForDate(Date date, int offset, int max) {
date.clearTime()
def c = Event.createCriteria()
def results = c {
and {
le("startDate", date+1) // starts tonight at midnight or prior?
ge("endDate", date) // ends today or later?
}
maxResults(max)
order("startDate", "desc")
}
return results
}
The Spock Specification
package myapp
import grails.plugin.spock.*
import spock.lang.*
class EventServiceSpec extends Specification {
def event
def eventService = new EventService()
def setup() {
event = new Event()
event.publisher = Mock(User)
event.title = 'et'
event.urlTitle = 'ut'
event.details = 'details'
event.location = 'location'
event.startDate = new Date(2010,11,20, 9, 0)
event.endDate = new Date(2011, 3, 7,18, 0)
}
def "list the Events of a specific date"() {
given: "An event ranging over multiple days"
when: "I look up a date for its respective events"
def results = eventService.listEventsForDate(searchDate, 0, 100)
then: "The event is found or not - depending on the requested date"
numberOfResults == results.size()
where:
searchDate | numberOfResults
new Date(2010,10,19) | 0 // one day before startDate
new Date(2010,10,20) | 1 // at startDate
new Date(2010,10,21) | 1 // one day after startDate
new Date(2011, 1, 1) | 1 // someday during the event range
new Date(2011, 3, 6) | 1 // one day before endDate
new Date(2011, 3, 7) | 1 // at endDate
new Date(2011, 3, 8) | 0 // one day after endDate
}
}
The Error
groovy.lang.MissingMethodException: No signature of method: static myapp.Event.createCriteria() is applicable for argument types: () values: []
at myapp.EventService.listEventsForDate(EventService.groovy:47)
at myapp.EventServiceSpec.list the Events of a specific date(EventServiceSpec.groovy:29)
You should not use unit tests to test persistence - you're just testing the mocking framework.
Instead, move the criteria query to an appropriately named method in the domain class and test it against a database with an integration test:
class Event {
...
static Set<Event> findAllEventsByDay(Date date, int offset, int max) {
...
}
}
class EventService {
Set<Event> listEventsForDate(Date date, int offset, int max) {
...
return Event.findAllEventsByDay(date, offset, max)
}
}
If there's still value in having the service method as a wrapper (e.g. if it implements some business logic above and beyond the database query), it will now be easy to unit test since it's trivial to mock out the static domain class method call:
def events = [new Event(...), new Event(...), ...]
Event.metaClass.static.findAllEventsByDay = { Date d, int offset, int max -> events }
And that's appropriate since you're testing how the service uses the data it receives and assuming that the retrieval is covered in the integration tests.
Criteria queries are not supported in unit tests. From the mockDomain documentation:
[T]he plugin does not support the mocking of criteria or HQL queries. If you use either of those, simply mock the corresponding methods manually (for example with mockFor() ) or use an integration test with real data.
You'll have to make your test an integration test. You'll see that the exception goes away if you move the test from the test/unit folder to the test/integration folder.
There is some work being done on criteria support in unit tests, and if you're feeling adventurous, you can try it out today. See this mailing list discussion of the DatastoreUnitTestMixin.