Grails async integration testing - grails

I've a Grails 2.5.6 project with a rest controller in which I create n async tasks each of them invoking a method in a service like this:
// MyController
...
def statuses = ['where', 'ownership', 'store']
def tasks = statuses.collect { st ->
task {
return myService.invokeMethod(st, [aDomain, data])
}
}
def props = waitAll(tasks)
...
// MyService
...
store(aDomain, data) {
...
def store = Store.get(data.store)
...
}
If I execute the application, the Store is correctly found in the database.
I've also created an integration test. To avoid conflicts with existing data in the database I create a new Store during the test:
// Integration test (simplified)
....
def store = new Store(....)
store.save(flush: true)
...
def json = [store: store.id] as JSON
...
controller.request.content = params.toString()
controller.request.method = "POST"
controller.update()
...
If I execute the tests, the new store created is not found in the service and the test fails.
I've verified the situation in some points of the application and found that:
- if I search the store in the controller (before or after the tasks are executed), it is found
- if I list all the stores in the service method the new store doesn't exist.
I suppose that this behaviour is due to how hibernate session is handled during tests but I don't know how to solve it.
Any suggestion is welcome.
Thanks

Try using Synchronouse promise factory for tests.
void setup() {
Promises.promiseFactory = new SynchronousPromiseFactory()
}

Related

Grails 3 bootstrap - service doesn't create db entry

I have a very simple service set up to create an entry in a Postgres table, and I use it in Bootstrap.groovy for my Grails 3 web-app.
// CompanyService
public Company createCompany(String name) {
Company company = new Company(name: name)
company.save()
return company
}
// BootStrap
def init = {
companyService.createCompany('My Company')
}
Well, at startup I cannot see My Company entry, no matter if the service is transactional or not.
Instead, if using the same line for example in a controller, it works as expected. Am I missing something here?
Have you called your service inside bootstrap?
class BootStrap {
def companyService
def init = { servletContext ->
companyService.createCompany('My Company')
}
}

Grails: Saving domain models with GORM inside an async task

I'm on Grails 2.3.5 and I'm saving data with gorm inside a task block.
When saving the domain model the task hangs.
Here an excerpt:
task {
def j = new Job()
j.name = "test"
j.save()
println "saved !" // never get here
}
Any idea on how to save domain models inside a task block ?
Thanks
Luca
Try the variation below. It's worth noting that there are nuances to be aware of concerning async threads and Hibernate sessions. See this section in the ref docs for more.
Job.async.task {
def j = new Job()
j.name = "test
j.save()
...
}
I'm posting here the solution:
task {
Job.withNewSession {
def j = new Job()
j.name = "test"
j.save()
...
}
}

Putting an object on a Request in a Grails Integration Test

In a Grails project I am looking at here, a filter puts a Domain object on the request...
class TokenFilters {
def filters = {
all( uri: '/hiphop/**' ) {
before = {
MyToken myToken = ...
request.myToken = myToken
MyToken looks like:
class MyToken {
String id
String token
static mapping = {
token( index: true )
id( generator: 'uuid' )
}
...
}
In my controller, the myToken is pulled off the request.
MyController {
myaction {
MyToken accessToken = request.myToken
All fine. I wish to write an integration test for the controller.
#Test
void testLogin() {
def mc = new MyController()
def myToken = new MyToken(1234);
// set the request parameters
mc.request.parameters = [myToken:myToken];
def message = mc.action();
assertTrue(message.indexOf("trans") > 0)
}
When I run this, I get:
Failure: testLogin(MyTests)
| java.lang.IllegalArgumentException: Parameter map value must be single value or array of type [java.lang.String]
at testLogin(MyTests.groovy:40)
So it looks like Grails will only let me a String or a single value and doesn't like me putting an object on the request in the Filter. Even thou it lets me put on the same object type in a Filter.
I'd really like to test this without going to Functional tests. Please help. I am using Grails 2.2.1
Thanks
The problem is that your code is passing parameters to the controller. Your emulating an HTTP request which can't handle objects. What you can do is:
mc.request.parameters = [myToken: '1234']
and then you're controller/filter would pull out the 1234 and look up MyToken. If you were testing the controller forwarding then you can put objects in the request. Not the other way around.
I see now that part of the problem is that you're trying to test a controller that is assuming data coming from a filter.
You've omitted some code, but assuming you are extending ControllerUnitTestCase then you have access to a mock request object. You should be able to simply do:
#Test
void testLogin() {
def mc = new MyController()
def myToken = new MyToken(1234);
// set the request parameters
request.myToken = myToken
def message = mc.action();
assertTrue(message.indexOf("trans") > 0)
}

Writing an Integration Test in Grails that will test DB persistence

My Integration-Test for my grails application is returning a null object when I try to get a domain object using grails dynamic get method.
This is a simplified example of my problem. Lets say I have a controller TrackerLogController that uses a service TrackerLogService to save an updated Log domain for another Tracker domain.
Domain Tracker:
class Tracker {
int id
String name
static hasMany = [logs: Log]
}
Domain Log:
class Log {
int id
String comment
static belongsTo = [tracker: Tracker]
}
Controller TrackerLogController save:
def TrackerLogService
def saveTrackerLog() {
def trackerId = params.trackerId
def trackerInstance = Tracker.get(trackerId)
Log log = TrackerLogService.saveTrackerLogs(trackerInstance, params.comment)
if( log.hasErrors() ){
//render error page
}
//render good page
}
Service TrackerLogService save:
Log saveTrackerLogs( Tracker tracker, String comment) {
Log log = new Log(tracker: tracker, comment: comment)
log.save()
return log
}
So now I want to write an Integration-Test for this service but I'm not sure if I should be writing one just for the simple logic in the controller (if error, error page else good page) I would think I would write a Unit test for that, and an Integration-Test to check the persistence in the Database.
This is what I have for my Integration-Test:
class TrackerLogServiceTests {
def trackerLogService
#Before
void setUp(){
def tracker = new Tracker(id: 123, name: "First")
tracker.save()
//Now even if I call Tracker.get(123) it will return a null value...
}
#Test
void testTrackerLogService() {
Tacker trackerInstance = Tracker.get(123) //I have tried findById as well
String commit = "This is a commit"
//call the service
Log log = trackerLogService.saveTrackerLogs(trackerInstance , commit)
//want to make sure I added the log to the tracker Instance
assertEquals log , trackerInstance.logs.findByCommit(commit)
}
}
So for this example my trackerInstance would be a null object. I know the Grails magic doesn't seem to work for Unit tests without Mocking, I thought for Intigration-Tests for persistence in the DB you would be able to use that grails magic.
You can't specify the id value unless you declare that it's "assigned". As it is now it's using an auto-increment, so your 123 value isn't used. It's actually ignored by the map constructor for security reasons, so you'd need to do this:
def tracker = new Tracker(name: "First")
tracker.id = 123
but then it would get overwritten by the auto-increment lookup. Use this approach instead:
class TrackerLogServiceTests {
def trackerLogService
private trackerId
#Before
void setUp(){
def tracker = new Tracker(name: "First")
tracker.save()
trackerId = tracker.id
}
#Test
void testTrackerLogService() {
Tacker trackerInstance = Tracker.get(trackerId)
String commit = "This is a commit"
//call the service
Log log = trackerLogService.saveTrackerLogs(trackerInstance , commit)
//want to make sure I added the log to the tracker Instance
assertEquals log , trackerInstance.logs.findByCommit(commit)
}
}
Also, unrelated - don't declare the id field unless it's a nonstandard type, e.g. a String. Grails adds that for you, along with the version field. All you need is
class Tracker {
String name
static hasMany = [logs: Log]
}
and
class Log {
String comment
static belongsTo = [tracker: Tracker]
}

hbase - how to change tables structure without drop tables

i use grails-1.3.2 and gorm-hbase-0.2.4 plugin.
Sometimes i need to change tables structure(add new tables or columns).
I have created Car table:
class Car{
static belongsTo = [user:User]
String color
String model
//.....
static constraints = {
}
}
but when i want to create car object:
def create = {
Car car = new Car()
car.properties = params
car.save(flush: true)
}
I got the following exception:
ERROR gorm.SavePersistentMethod - APP_CAR
org.apache.hadoop.hbase.TableNotFoundException: APP_CAR
After i run application with create-drop, everithing starts work good..
but i can not after every changes delete all data,
i thought plugin have to do all updates
so, i am looking some way after changind tables structure continue to run application without drop tables..
If anybody know solution please help.
Grails will NOT do automatic updates to your tables, what if it drops a column in production automatically? Maybe that is not what you wanted.
There is a database migration plugin to do this and here is an excellent link that explains it. Note that you need to use grails prod instead of using the ones directly in the link, otherwise it will run in development mode only. The link does not show prod in its commands.
The official links are here and the spring source blog about this is here.
database migration plugin will not be works, because it works only with hibernate.
You need to do some changes in plugin source. HBasePluginSupport.grovy
static doWithApplicationContext = {ApplicationContext applicationContext ->
LOG.debug("Closure HBasePluginSupport.doWithApplicationContext{} invoked with arg $applicationContext")
assert !PluginManagerHolder.getPluginManager().hasGrailsPlugin("hibernate"),"hibernate plug-in conflicts with gorm-hbase plug-in"
// Read data source configuration, setting defaults as required
def dataSource = application.config.dataSource
// TODO write tests for this <--- Even maybe figure out if this is ever invoked
if (!dataSource) dataSource = new HBaseDefaults()
def dbCreate = dataSource?.dbCreate
if (!dbCreate) dbCreate = "create-drop"
LOG.debug("Data Source configured with dbCreate set to $dbCreate")
// TODO Complete dbCreate related processing
if (dbCreate?.toUpperCase()?.equals("CREATE-DROP")) {
def createIndexedTables = dataSource?.indexed
LOG.debug ("Flag createIndexedTables set to $createIndexedTables")
def tableManager = HBaseLookupUtils.getBean("hbase.table.manager")
tableManager.createSequenceTable()
tableManager.createReferenceTable()
application.domainClasses.each {domainClass ->
LOG.debug("Adding table for Domain Class $domainClass")
tableManager.createDomainTable(domainClass, createIndexedTables)
}
LOG.debug("List of all store found :")
tableManager.getTableNames().each {tn ->
LOG.debug("- $tn")
}
} else if (dbCreate?.toUpperCase()?.equals("UPDATE")) {
def createIndexedTables = dataSource?.indexed
def tableManager = HBaseLookupUtils.getBean("hbase.table.manager")
def existingTables = tableManager.getTableNames();
application.domainClasses.each {domainClass ->
LOG.debug("Domain Class $domainClass")
def tableDesc = new HTableDescriptor(HBaseNameUtils.getDomainTableName(domainClass))
if (!existingTables.contains(tableDesc.getNameAsString())) {
tableManager.createDomainTable(domainClass, createIndexedTables)
LOG.debug("Adding table for Domain Class $domainClass")
}
}
}
application.domainClasses.each {domainClass ->
LOG.debug("Adding dbms related methods to Domain Class $domainClass")
def domainClassManager = new HBaseDomainClassManager()
domainClassManager.createQueryMethods(domainClass)
domainClassManager.createPersistenceMethods(domainClass)
domainClassManager.addLazyLoadingSupport(domainClass)
domainClassManager.addDynamicFinders(domainClass)
}
}

Resources