Details
Description
Two of the below tests fails:
class ValidateableTests {
@Test
void testNotAnnotatedButMocked() {
mockForConstraintsTests(NotAnnotated)
assert !new NotAnnotated().validate()
} // Success
@Test
void testAnnotatedAndMocked() {
mockForConstraintsTests(Annotated)
assert !new Annotated().validate()
} // Failure
@Test
void testAnnotatedButNotMocked() {
assert !new Annotated().validate()
} // Error - IllegalArgumentException: ServletContext must not be null
}
class NotAnnotated {
String property
static constraints = {
property nullable: false
}
}
@Validateable
class Annotated {
String property
static constraints = {
property nullable: false
}
}
GRAILS-8803, and GRAILS-9147 might be related.
-
Hide
- a-bug-report-10092012.zip
- 10/Sep/12 9:53 AM
- 23 kB
- Ken Krebs
-
- grails-app/.../ApplicationResources.groovy 0.1 kB
- grails-app/conf/BootStrap.groovy 0.1 kB
- grails-app/conf/BuildConfig.groovy 2 kB
- grails-app/conf/Config.groovy 4 kB
- grails-app/conf/DataSource.groovy 1 kB
- grails-app/conf/UrlMappings.groovy 0.2 kB
- grails-app/conf/spring/resources.groovy 0.0 kB
- grails-app/.../SearchController.groovy 0.3 kB
- grails-app/i18n/messages.properties 3 kB
- grails-app/.../messages_cs_CZ.properties 3 kB
- grails-app/i18n/messages_da.properties 3 kB
- grails-app/i18n/messages_de.properties 4 kB
- grails-app/i18n/messages_es.properties 3 kB
- grails-app/i18n/messages_fr.properties 2 kB
- grails-app/i18n/messages_it.properties 2 kB
- grails-app/i18n/messages_ja.properties 4 kB
- grails-app/i18n/messages_nl.properties 3 kB
- grails-app/.../messages_pt_BR.properties 3 kB
- grails-app/.../messages_pt_PT.properties 3 kB
- grails-app/i18n/messages_ru.properties 4 kB
- grails-app/i18n/messages_sv.properties 3 kB
- grails-app/i18n/messages_th.properties 6 kB
- grails-app/.../messages_zh_CN.properties 2 kB
- grails-app/views/error.gsp 0.3 kB
- grails-app/views/index.gsp 3 kB
- grails-app/views/layouts/main.gsp 2 kB
- src/groovy/a/SearchCommand.groovy 0.2 kB
- test/.../SearchCommandIntegrationTests.groovy 0.5 kB
- test/unit/.../SearchCommandUnitTests.groovy 0.5 kB
- application.properties 0.1 kB
Issue Links
- is duplicated by
-
GRAILS-9380
constraint validation tests do not work on a standalone command object
-
Activity
- All
- Comments
- Work Log
- History
- Activity
- Git Commits
We're seeing the same issue here too.
To further compound this problem IntelliJ Idea requires you to specify @Validatable on the command object - even if it lives in the same package as the controller and the class ends with the word 'Command'. If you don't use the annotation it won't even compile the project and complains this things like this:
The [useMyCommand] action accepts a parameter of type [MyCommand] which does not appear to be a command object class. This can happen if the source code for this class is not in this project and the class is not marked with @Validateable.
Dominic,
Being in the same package as a controller is not supposed to have any effect, nor is having a class name that ends in 'Command'. Those 2 things don't have anything to do with making a class a command object and/or making it validateable.
Dominic,
Grails 2.0.4 does not require that command object classes be in the same package as any controller and does not require that the command object class is marked with @Validateable. If a controller action accepts a statically typed parameter Grails will attempt to configure that parameter type as a command object. If the class has already been marked with @Validateable, then it will be validateable. If the class is not marked @Validateable and is defined in the same source file as a controller, that class will be made validateable. If the class is not marked @Validateable and is not defined in a controller source file, the class can still be used as a command object but it will not be validateable. It is not a requirement that command object classes be validateable. For cases like that the Grails compiler will issue a warning like this:
Warning The [index] action accepts a parameter of type [com.demo.DemoCommand] which has not been marked with @Validateable. Data binding will still be applied to this command object but the instance will not be validateable.
def index(DemoCommand co) {
^
That is just a warning. The code still compiles and runs.
If IntelliJ will not allow the controller to be compiled in a situation like that, then there may be an issue with IntelliJ. Does the compiler error mentioned above happen only when building from the IDE or does it happen when building the application from a command line?
Jeff,
I am also seeing this issue in Grails 2.1.
Domain classes validate OK
Objects configured as validateable via Config.groovy are ok
Objects annotated by @Validateble are not.
Note, that in my case I am not using the object as a command object. Its just a an object in src/groovy/somePackage to which I am manually binding and validating in a controller action.
I've traced the problem through n Grails 2.1 and something bad is happening in GrailsClassUtils.getStaticPropertyValue(Class<?> clazz, String name) when it tries to find the constraints.
MockUtils.addValidateMethod calls GrailsClassUtils.getStaticPropertyValue(clazz, "constraints")
That call looks like this:
The code looks like:
public static Object getStaticPropertyValue(Class<?> clazz, String name) { Method getter = BeanUtils.findDeclaredMethod(clazz, getGetterName(name), null); try { if (getter != null) { return getter.invoke(null); } return getStaticFieldValue(clazz, name); } catch (Exception ignored) { // ignored } return null; }
As the result of the Validateable annotation the "return null" at the end of the method is being called, which only happen if an Exception is raised (and swallowed).
So I think an unexpected Exception is being thrown as a result of the @Validateable annotation but nothing is reported, and so the constraints property of the class is returning no result.
If you use Config.groovy to specify the class as validateable, then the GrailsClassUtils.getStaticPropertyValue(clazz, "constraints") call works and you get the constraints closure back.
Cheers
Neill
Jeff,
Just to be clear, this happens in command line as well as IDEA.
I think this should be promote to Blocker. Even though there is a workaround, its not ideal.
Cheers
N
Ken,
Your SearchController looks like this:
package a class SearchController { // def search1(SearchCommand cmd) { // def valid = cmd.validate() // render "search1 : $valid" // } def search2() { SearchCommand cmd = new SearchCommand(params) def valid = cmd.validate() render "search2 : $valid" } }
In both cases (the commented out search1 action and the search2 action) you are calling .validate() on the command object. Are you aware that when the framework creates an instance of a command object for you and passes it into the controller action that the command object has already been validated?
Ken,
There are issues with testing @Validateable objects and that is why this JIRA is still open. However, you can do something like this to test the validity of your command object...
package a import grails.validation.Validateable import groovy.transform.ToString @Validateable @ToString class SearchCommand { String s static constraints = { s(nullable: false, blank: true) } }
package a class SearchController { def search1(SearchCommand cmd) { render "search1 : ${!cmd.hasErrors()}" } }
package a @TestFor(SearchController) class SearchControllerTests { void testInvalidCommandObjectState() { controller.search1() assert response.text == 'search1 : false' } void testValidCommandObjectState() { params.s = 'some string' controller.search1() assert response.text == 'search1 : true' } }
I hope that helps.
I believe I am able to shed some light on this. I put in the duplicate ticket GRAILS-9380 and am using 2.0.4.
I saw 1 of the previous comments on the grails.validateable.classes property workaround and was able to get stuff working based on info gained from that.
I have attached a sample app that has a command,controller, unit, and integration tests. The 2 tests are identical except for the class name and location.
For any of the tests to work it was necessary to annotate with @TestMixin(GrailsUnitTestMixin) else mockForConstraintsTests() is a MissingMethod. The physical location of the command object in the same package as the controller or in src/groovy seemed to make no difference.
I tried all of the combinations of the grails.validateable.classes property and using the @Validateable. Note the 2 different actions in the SearchController. search1 takes the SearchCommand as a method parameter while search2 creates it on the fly from the params. The Integration test always works using the TestMixin.
The search1 method is a problem regardless. If @Validateable is not used, search1 will not compile.
I'm using GroovyAndGrails ToolSuite so the problem isn't limited to IDEA. The error message is:
"Groovy:The [search1] action accepts a parameter of type [a.SearchCommand] which does not appear to be a
command object class. This can happen if the source code for this class is not in this project and the class is not
marked with @Validateable."
If @Validateable is used, the unit test fails regardless and the search1 action doesn't work right in the webapp.
The happiest workaround for me is to use neither @Validateable nor the Config property and use the @TestMIxin. In this way, the search2 technique works and so does the unit test.
This is a minor issue for me (now that I know how to deal with it) but probably should be fixed and/or documentation appropriately updated to avoid others being caught up in this. It's nice to be able to pass in the command object rather than creating it as testing the controller action is easier but I can live without it. Apparently merely having the constraints specified on the command object is enough for it to work.
Hope this helps. BTW, I am loving Grails2 ;=)
Ken Krebs
This still appears to be a problem in 2.2.0. Ken's workarounds seem to do the trick for me. Thanks, Ken!
The only workaround I've found is to instead of annotating the class with @Validateable, configure it in Config.groovy:
grails.validateable.classes = [test.NotAnnotated]