Grails

Bootstrap doesn't be called at test-app command

Details

  • Type: Bug Bug
  • Status: Closed Closed
  • Priority: Major Major
  • Resolution: Fixed
  • Affects Version/s: 1.0-RC3
  • Fix Version/s: 1.1-RC1
  • Component/s: Configuration
  • Labels:
    None
  • Environment:
    ubuntu 7.10, sun-jdk6, grails 1.0-rc3
  • Testcase included:
    yes

Description

I add codes below in init closure of grails-app/conf/BootStrap.groovy
String.metaClass.newReader = {->
new StringReader(delegate)
}

And codes below in one Test file:
void testSomething() { String fileContents = "data.log" Reader reader = fileContents.newReader() }

When I run "grails test-app", it is failed because no newReader method for String class.
But If I run grails shell, and input "aaa".newReader(), it works fine.
So I think the bootstrap class doesn't be called at test-app command.

  1. TestApp.patch
    08/Apr/08 3:42 PM
    1 kB
    Corey

Activity

Hide
Corey added a comment -

This came up today on grails-user: http://www.nabble.com/test-app-BootStrap--to16570647.html

A potential solution was provided, the resulting patch attached.

I don't know whether it should occur only when integrationOnly is true, or what other issues may need thinking through; and the patch has hardly been tested.

Show
Corey added a comment - This came up today on grails-user: http://www.nabble.com/test-app-BootStrap--to16570647.html A potential solution was provided, the resulting patch attached. I don't know whether it should occur only when integrationOnly is true, or what other issues may need thinking through; and the patch has hardly been tested.
Hide
Corey added a comment -

potential patch suggestion for having integration tests run BootStrap

Show
Corey added a comment - potential patch suggestion for having integration tests run BootStrap
Hide
Corey added a comment -

Changed patch so that BootStrap is only executed if theres more than 0 integration tests – that way running: 'grails test-app' won't
execute the BootStrap when there's only unit tests.

Show
Corey added a comment - Changed patch so that BootStrap is only executed if theres more than 0 integration tests – that way running: 'grails test-app' won't execute the BootStrap when there's only unit tests.
Hide
Graeme Rocher added a comment -

I'm not sure it is wise to call bootstrap during test-app for integration tests. Bootstrap is typically used to setup production data, why would this be relevant in a test?

Show
Graeme Rocher added a comment - I'm not sure it is wise to call bootstrap during test-app for integration tests. Bootstrap is typically used to setup production data, why would this be relevant in a test?
Hide
Corey added a comment -

I'll explain the situation as I perceived it, and the circumstances which brought me to the desire to use BootStrap (or a BootStrap... TestBootStrap.groovy?).

For one, I see that test is an environment, just like development and prod, and you can test for environment in BootStrap and execute code as appropriate to the environment - so, I wasn't thinking of BootStrap as "production data only", but rather "data initialization for all environments and/or specific environments".

Then secondly, I have alot of fixture data that my app requires for integration testing - initializing all that data in base fixture classes has become ridiculously tedious - made worse because all the initialization necessary for my integration tests exactly mirrors the same data that I use in BootStrap for a prestine product app - made doubly worse because all that data gets re-initialized on every single test.

An example of the sort of initialization I need for integration tests and for a prestine instance of the app:

// UserStatus
new UserStatus( name: 'void' )
.addToStates( new UserState( name: 'pending' ) )
.addToStates( new UserState( name: 'exception' ) )
.addToStates( new UserState( name: 'voided' ) )
.save(flush:true)

new UserStatus( name: 'active' )
.addToStates( new UserState( name: 'enabled' ) )
.addToStates( new UserState( name: 'disabled' ) )
.save(flush:true)

... now, I have about 10 of those (ServiceOrderState, AccountStatus, etc. etc) - with many of them having far more States and Status'; and that's not including similar setups for Groups and Roles and a default superuser, and the required SiteConfiguration; and other stuff like: Site.metaClass.'static'.getSiteName = { -> Site.findAll()[0]?.siteName } .

So not only do I have to repeat all that in base fixtures for my tests, but it all gets-re-initialized for every test.

Without being able to run a BootStrap for integration tests, I would need to:

0 - put data-initialization into BootStrap
1 - 'grails development run-app'
2 - manually import MyApp_dev db to MyApp_test
3 - 'grails test-app MyIntegrationTest'
4 - repeat everytime I need to change the fixture data

... it was when I found myself about to begin the above process, that I realized: "I should really be able to use BootStrap..., this is redundant and frustrating"

And to show just how gnarly my integration fixtures have gotten, along with all that base-level fixture data I need, there's the issue where many of my integration tests are rather complex in their associations:

To test a Transaction, I need all TransactionStates and TransactionStatus, and a Transaction also needs a ServiceOrder, ServiceOrder needs This and That and FooBar, FooBar needs Blah and Goober... etc. etc. – so part of my test environment BootStrap would be to create and store default/test entities so that doing an integration tests such as 'TransactionTests.testExecuteTransaction'() doesn't require a boat load of complex setUp's and multiple base fixtures.

So the issues - as I see them - that I'm running into without integration tests being able to run BootStrap, are: dry violation, innefficiency, inconviencience

If it's actually my practices that are the actual underlying problem, I would appreciate any advice on the matter - thanks!

Show
Corey added a comment - I'll explain the situation as I perceived it, and the circumstances which brought me to the desire to use BootStrap (or a BootStrap... TestBootStrap.groovy?). For one, I see that test is an environment, just like development and prod, and you can test for environment in BootStrap and execute code as appropriate to the environment - so, I wasn't thinking of BootStrap as "production data only", but rather "data initialization for all environments and/or specific environments". Then secondly, I have alot of fixture data that my app requires for integration testing - initializing all that data in base fixture classes has become ridiculously tedious - made worse because all the initialization necessary for my integration tests exactly mirrors the same data that I use in BootStrap for a prestine product app - made doubly worse because all that data gets re-initialized on every single test. An example of the sort of initialization I need for integration tests and for a prestine instance of the app: // UserStatus new UserStatus( name: 'void' ) .addToStates( new UserState( name: 'pending' ) ) .addToStates( new UserState( name: 'exception' ) ) .addToStates( new UserState( name: 'voided' ) ) .save(flush:true) new UserStatus( name: 'active' ) .addToStates( new UserState( name: 'enabled' ) ) .addToStates( new UserState( name: 'disabled' ) ) .save(flush:true) ... now, I have about 10 of those (ServiceOrderState, AccountStatus, etc. etc) - with many of them having far more States and Status'; and that's not including similar setups for Groups and Roles and a default superuser, and the required SiteConfiguration; and other stuff like: Site.metaClass.'static'.getSiteName = { -> Site.findAll()[0]?.siteName } . So not only do I have to repeat all that in base fixtures for my tests, but it all gets-re-initialized for every test. Without being able to run a BootStrap for integration tests, I would need to: 0 - put data-initialization into BootStrap 1 - 'grails development run-app' 2 - manually import MyApp_dev db to MyApp_test 3 - 'grails test-app MyIntegrationTest' 4 - repeat everytime I need to change the fixture data ... it was when I found myself about to begin the above process, that I realized: "I should really be able to use BootStrap..., this is redundant and frustrating" And to show just how gnarly my integration fixtures have gotten, along with all that base-level fixture data I need, there's the issue where many of my integration tests are rather complex in their associations: To test a Transaction, I need all TransactionStates and TransactionStatus, and a Transaction also needs a ServiceOrder, ServiceOrder needs This and That and FooBar, FooBar needs Blah and Goober... etc. etc. – so part of my test environment BootStrap would be to create and store default/test entities so that doing an integration tests such as 'TransactionTests.testExecuteTransaction'() doesn't require a boat load of complex setUp's and multiple base fixtures. So the issues - as I see them - that I'm running into without integration tests being able to run BootStrap, are: dry violation, innefficiency, inconviencience If it's actually my practices that are the actual underlying problem, I would appreciate any advice on the matter - thanks!
Hide
David Buschman added a comment -

I have the same situation as Corey above.

I need to run Bootstrap on any database that does not have a specific set of domain objects populated in it for a minimal 'runnable' configuration. This minimal initial setup is needed for any test code to run or any browser end user to operate on the application in production.
I have the code smart enough to detect when the users have modified the configuration from the default and thus not perform any inserts to prevent deleted object from re-appearing.

The bootstrap process should only run once per database startup in test mode so adding it to the setUp() method of each test is not desirable.

The needed domain objects will be inserted every time in test mode(memory database) and only once in production(real DB).
I added the following code the TestApp.groovy(grails 1.0.1) around line 328

populateTestSuite(suite, testFiles, classLoader, appCtx, "test/integration/")
if (suite.testCount() > 0) {

+ // 1-time initialization for integration tests via BootStrap
+ GrailsConfigUtils.executeGrailsBootstraps(app, appCtx, servletContext )

int testCases = suite.countTestCases()

NOTE: Also added import for GrailsConfigUtils

Show
David Buschman added a comment - I have the same situation as Corey above. I need to run Bootstrap on any database that does not have a specific set of domain objects populated in it for a minimal 'runnable' configuration. This minimal initial setup is needed for any test code to run or any browser end user to operate on the application in production. I have the code smart enough to detect when the users have modified the configuration from the default and thus not perform any inserts to prevent deleted object from re-appearing. The bootstrap process should only run once per database startup in test mode so adding it to the setUp() method of each test is not desirable. The needed domain objects will be inserted every time in test mode(memory database) and only once in production(real DB). I added the following code the TestApp.groovy(grails 1.0.1) around line 328 populateTestSuite(suite, testFiles, classLoader, appCtx, "test/integration/") if (suite.testCount() > 0) { + // 1-time initialization for integration tests via BootStrap + GrailsConfigUtils.executeGrailsBootstraps(app, appCtx, servletContext ) int testCases = suite.countTestCases() NOTE: Also added import for GrailsConfigUtils
Hide
David Buschman added a comment -

Graeme,

What if the grails-app/conf/Bootstrap.groovy was changed to one of the following :

class BootStrap {

def init = { servletContext -> }

def initProduction = { servletContext -> init(servletContext) }

def initTest = { servletContext -> }

def initDevelopement { servletContext -> }

def destroy = {

}
}

or

import grails.util.GrailsUtil

class BootStrap {

def init = { servletContext -> }

def destroy = {

}

def initialize = { servletContext ->

switch(GrailsUtil.environment) { case "development": break case "test": break case "production": init( servletContext) break }

}

}

Then the executeGrailsBootstap code would call the initialize closure instead of the init closure to do the configuration.

Also when grails upgrade is run, these methods could be added without affecting the current function and behavior of the various run modes.

This way the existing apps can continue as they are behaving now and new apps will get the new boilerplate. Both can be configured as needed specific to the application after the upgrade.

Show
David Buschman added a comment - Graeme, What if the grails-app/conf/Bootstrap.groovy was changed to one of the following : class BootStrap { def init = { servletContext -> } def initProduction = { servletContext -> init(servletContext) } def initTest = { servletContext -> } def initDevelopement { servletContext -> } def destroy = { } } or import grails.util.GrailsUtil class BootStrap { def init = { servletContext -> } def destroy = { } def initialize = { servletContext -> switch(GrailsUtil.environment) { case "development": break case "test": break case "production": init( servletContext) break } } } Then the executeGrailsBootstap code would call the initialize closure instead of the init closure to do the configuration. Also when grails upgrade is run, these methods could be added without affecting the current function and behavior of the various run modes. This way the existing apps can continue as they are behaving now and new apps will get the new boilerplate. Both can be configured as needed specific to the application after the upgrade.
Hide
Lee Butts added a comment -

I've come across this issue recently as we are using Bootstrap to create some basic test data and run an automatic Liquibase migration.

Corey's patch seems to work well, and if it did cause people issues that can wrap prod-only init code in an if(GrailsUtil.environment == .. type line.

Until Grails gets an equivalent of Rails' Fixture functionality I think we need to allow for Bootstrap to be used to create test data for integration tests

Show
Lee Butts added a comment - I've come across this issue recently as we are using Bootstrap to create some basic test data and run an automatic Liquibase migration. Corey's patch seems to work well, and if it did cause people issues that can wrap prod-only init code in an if(GrailsUtil.environment == .. type line. Until Grails gets an equivalent of Rails' Fixture functionality I think we need to allow for Bootstrap to be used to create test data for integration tests
Hide
Marc Palmer added a comment -

This is completely counter-intuitive IMO.

BootStrap should run for integration tests.

Show
Marc Palmer added a comment - This is completely counter-intuitive IMO. BootStrap should run for integration tests.
Hide
Ray Tayek added a comment -

i just ran into this trying to use test data created by bootstrap when testing. seems like one would want some kind of super one-time SuiteFixture Setup: http://xunitpatterns.com/SuiteFixture%20Setup.html and the corresponding teardown. this would decouple the testing setup and teardown from boostrap.

thanks

Show
Ray Tayek added a comment - i just ran into this trying to use test data created by bootstrap when testing. seems like one would want some kind of super one-time SuiteFixture Setup: http://xunitpatterns.com/SuiteFixture%20Setup.html and the corresponding teardown. this would decouple the testing setup and teardown from boostrap. thanks
Hide
Peter Ledbrook added a comment -

Current consensus is to leave this for 1.1 because it will be a breaking change.

Show
Peter Ledbrook added a comment - Current consensus is to leave this for 1.1 because it will be a breaking change.
Hide
Graeme Rocher added a comment - - edited

There are enough votes on this issue and people suffering from it to justify the change. I will add it to the breaking change list, in the meantime users will have to change their bootstraps too:

import grails.util.*

if(Environment.current = Environment.PRODUCTION) {
   //
}
Show
Graeme Rocher added a comment - - edited There are enough votes on this issue and people suffering from it to justify the change. I will add it to the breaking change list, in the meantime users will have to change their bootstraps too:
import grails.util.*

if(Environment.current = Environment.PRODUCTION) {
   //
}

People

Vote (12)
Watch (7)

Dates

  • Created:
    Updated:
    Resolved: