I'm writing the Integration test for a Quartz Job in a grails application.
I've the Job in grails-app/jobs folder, and if I start the application it works. The problem is that I want to get it in an integration test, but the autowire won't work. The test is like:
class MyJobTest{
MyJob myJob
def setUp(){
assert myJob != null
}
def testExecute(){
//test logic
}
}
but it fails because myJob is null...some help?
Quartz Jobs are not autowired like services are under the test environment. The documentation for the Quartz job also explicitly states that by default it will not execute on schedule under the test environment (you could change that if you want to but I wouldn't). I would just instantiate myJob = new MyJob() in your setUp and call the execute() method to test it. If you're trying to test the triggers you may want to find a way to look at what is inside the triggers {} maybe inspecting the metaClass?
EDIT IN RESPONSE TO COMMENT:
I've never gotten the services out of the application context so that might work. The way I would probably test it is as follows:
Assuming your class looks something like this:
class MyJob {
def myServiceA
def myServiceB
def execute() {
if(myJobLogicToDetermineWhatToDo) {
myServiceA.doStuff(parameter)
} else {
myServiceB.doStuff(parameter)
}
}
}
What you're really wanting to test here is the myJobLogicToDetermineWhatToDo. I would assume that you have (or can easily write) integration and/or unit tests against your services myServiceA and myServiceB to ensure that they are working correctly. I would then write unit tests to test the logic/wiring of your Job to the appropriate service.
#Test
void routeOne() {
def job = new MyJob()
def myServiceA = new Object()
def expectedParameter = "Name"
def wasCalled = false
myServiceA.metaClass.doStuff = {someParameter ->
assert expectedParameter == someParameter
wasCalled = true
}
job.myServiceA = myServiceA
//Setup data to cause myServiceA to be invoked
job.execute()
assert wasCalled
}
Then repeat this process for all of the routes you have through your Job. This way you can isolate your tests down to the smallest part possible and test the logic of the object that you're invoking not the services it is using. I would assume you're using a service because the logic in there is being used by another part of the system. If you're testing the service through this job and for some reason the job goes away then you have to re-write your tests to invoke the service directly. The way that I've proposed you have tests testing the service directly and tests that mock out those service calls. If the job goes away you would simply delete the tests associated with it and you won't loose any test coverage. Kinda long winded but that's how I would approach testing it.
Related
I've a strange polluting situation between two spock integration tests that I'm not able to resolve. I suppose that I'm doing something wrong but I can't understand what.
The two integration tests are testing different situation of the same controller. In the first one I mock a service while in the second I don't mock.
Here are the significant parts of two tests :
test 1:
// CodeControllerSpec.groovy
...
def controller = new CodeController()
def serviceMock = new MockFor(PollutingService)
serviceMock.demand.search(1) { a, b, c ->
return [id: 1]
}
controller.myService.pollutingService = serviceMock.proxyInstance()
controller.save()
...
then:
serviceMock.verify(controller.myService.pollutingService)
test 2:
// CodeEngineSpec.groovy
...
def controller = new CodeController()
controller.show()
...
then:
...
Controller and services are as following
// CodeController
class CodeController extends RestfulController<Code> {
def myService
def show() {
...
myService.execute()
...
}
}
// MyService
class MyService {
def pollutingService
def execute() {
...
pollutingService.search(a, b, c)
...
}
}
// PollutingService
class PollutingService {
def search(a, b, c) {
...
...
}
}
If I run the two tests one by one, they all pass but, if I run them together, the second one fails with
No more calls to 'search' expected at this point. End of demands.
I'm sure that the mock in the first service is used (I've debugged code line by line) but I don't know why mock is not cleaned after test.
Any suggestion is really welcome.
I'm using grails 2.3.8
First of all, using mocks in integration tests has unpredictable results.
But putting that aside, where is controller.myService in your first test being instantiated? I would have expected that calling controller = new CodeController() would bypass autowiring of controller.myService.
I have grails 1.3.7 and quartz plugin 0.4.2 for cron jobs.
class MyJob {
static triggers = {}
void execute() {
//some doings.
}
}
Somewhere in my code I have dynamic scheduling of my job like that:
MyJob.schedule(cronExpression)
Every time I schedule new cron job it creates new one and it works together with previously created jobs. But I want to replace old cron jobs of MyJob with new one each time.
Maybe I was wrong in my understanding but I tried to add
def concurrent = false
It didn't help.
Is there any way to stop all jobs of some job class?
Thank you!
UPD: As I understand there is no such thing as InterruptableJob in this version of quartz plugin. Am I right?
Finally I've found way how to unschedule job and schedule another one.
First I've added a group for job:
class MyJob {
def group = 'jobGroupName'
static triggers = {}
void execute() {
// some stuff
}
}
Also I've found JobManagerService inside plugin with very useful methods. But I was looking into source codes to get how it works correctly. After some time my solutions become in next code (maybe it's raw yet, but it works):
class MyService {
def jobManagerService
def rescheduleJob() {
def job = jobManagerService.getJob('jobGroupName').first()
Scheduler scheduler = jobManagerService.quartzScheduler
Trigger trigger = scheduler.getTriggersOfJob(job, 'jobGroupName').first()
if (!jobManagerService.unscheduleJob(trigger.group, trigger.name)) {
log.warn('Failed during unscheduling job')
} else {
MyJob.schedule(newCronExpression)
}
}
}
It's not clear sometimes which name and group required in service methods. So debug and sources helped me with this.
So, I have this pretty simple Spec below. I have a class that is not a controller or service or anything like that. It's a Job class. It depends on two services: updateService and directoryTypeService. It runs a Redis async job and it's under /grails-app/jobs folder.
All I want is to make sure that whenever I invoke this job#perform() method (which return type is void), a given dependent method called UpdateService#completeClaiming is invoked, but UpdateService#requestNewPin is not. (Listing is a domain class, by the way).
When I run this Spec, I keep getting an error message saying: "No more calls to 'completeClaiming' expected at this point. End of demands."
What am I doing wrong here? Any wild guesses?
#Mock(Listing)
class SubmissionJobSpec extends Specification {
def directoryTypeServiceMock
def updateServiceMock
def job
def setup(){
job = new SubmissionJob()
directoryTypeServiceMock = mockFor(DirectoryTypeService)
updateServiceMock = mockFor(UpdateService)
job.updateService = updateServiceMock.createMock()
job.directoryTypeService = directoryTypeServiceMock.createMock()
}
def "if the directory is enabled and the pin status is ENTERED, we should call updateService.completeClaiming"() {
given:
directoryTypeServiceMock.demand.isUpdateEnabled { DirectoryType d, Country c -> return true}
new Listing(
location: new Location(country: Country.DE)
).save(failOnError: true, validate: false)
when:
job.perform(Listing.last().id, true)
then:
1 * updateServiceMock.completeClaiming(Listing.last(), true) >> new ListingEvent(output: [success: true])
0 * updateServiceMock.requestNewPin(_ as Listing, true)
}
You seem to be confusing Groovy and Spock mocks. You can't use Spock mocking syntax (e.g. 0 * updateServiceMock.requestNewPin(_ as Listing, true)) for a Groovy mock created with mockFor(). Spock mocks are created using Mock(), Stub() or Spy(). I'm not aware of any good reason to use a Groovy mock in a Spock spec.
I have a service method that locks a database row.
public String getNextPath() {
PathSeed.withTransaction { txn ->
def seed = PathSeed.lock(1)
def seedValue = seed.seed
seed.seed++
seed.save()
}
}
This is how my spock test looks like:
void "getNextPath should return a String"() {
when:
def path = pathGeneratorService.getNextPath()
then:
path instanceof String
}
It's just a simple initial test. However I get this error when I run the test:
java.lang.UnsupportedOperationException: Datastore [org.grails.datastore.mapping.simple.SimpleMapSession] does not support locking.
at org.grails.datastore.mapping.core.AbstractSession.lock(AbstractSession.java:603)
at org.grails.datastore.gorm.GormStaticApi.lock_closure14(GormStaticApi.groovy:343)
at org.grails.datastore.mapping.core.DatastoreUtils.execute(DatastoreUtils.java:302)
at org.grails.datastore.gorm.AbstractDatastoreApi.execute(AbstractDatastoreApi.groovy:37)
at org.grails.datastore.gorm.GormStaticApi.lock(GormStaticApi.groovy:342)
at com.synacy.PathGeneratorService.getNextPath_closure1(PathGeneratorService.groovy:10)
at org.grails.datastore.gorm.GormStaticApi.withTransaction(GormStaticApi.groovy:712)
at com.synacy.PathGeneratorService$$EOapl2Cm.getNextPath(PathGeneratorService.groovy:9)
at com.synacy.PathGeneratorServiceSpec.getNextPath should return a String(PathGeneratorServiceSpec.groovy:17)
Does anyone have any idea what this is?
The simple GORM implementation for Unit tests does not support some features, such as locking. Moving your test to an integration test will use the full implementation of GORM instead of the simple implementation used by unit tests.
Typically when you find yourself using anything more than the very basic features of GORM you will need to use integration tests.
Updated 10/06/2014
In more recent versions of Grails and GORM there is now the HibernateTestMixin which allows you to test/use such features in Unit tests. Further information can be found in the documentation.
As a workaround, I was able to get it working by using Groovy metaprogramming. Applied to your example:
def setup() {
// Current spec does not test the locking feature,
// so for this test have lock call the get method
// instead.
PathSeed.metaClass.static.lock = PathSeed.&get
}
I have a problem while running fixtureLoader.load in BootStrap.groovy.
import grails.plugin.fixtures.FixtureLoader
class BootStrap {
def fixtureLoader
def init = { servletContext ->
environments {
test {
fixtureLoader.load {
build {
device1(Device, name: "device1")
device2(Device, name: "device2")
device3(Device, name: "device3")
}
}
}
}
}
def destroy = {
}
}
When Grails starts integration test phase, the load is executed. Then when Grails starts functional test phase, the load is executed another time without cleaning previous execution.
Thats means:
If I run with "test-app functional:" or "test-app integration:" everything is all right.
If I run with "test-app" both functional and integration tests are executed.
This is the JSON representation of data at functional test phase (running with "test-app"):
[[name:device3], [name:device2], [name:device1],[name:device3], [name:device2], [name:device1]]
This is same JSON representation of data at functional test phase (running with "test-app functional:")
[[name:device3], [name:device2], [name:device1]]
How to avoid this duplicates?
Thanks in advance
The default test database is a non-persistant in-memory hsqldb that gets discarded at the end of your tests, but changes to it will carry over between test phases. Also, integration testing rolls back changes after each test, but this doesn't apply to database changes made in Bootstrap.groovy.
One simple way to fix this is to simple check for the existence of the fixtures before trying to create them. For example:
environments {
test {
if (Device.count() == 0) {
// build fixtures
}
}
}
Another possible solution is to use separate databases for integration and functional tests. There's an example of how to do it at http://www.redtoad.ca/ataylor/2011/02/setting-grails-functional-test-database/