grails domain-class validator troubles testing - unit-testing

I have a validator inside a domain class and I have a problem testing the controller for Lagerort.
com.example.xyz.LagerortControllerSpec > Test the update action performs an update on a valid domain instance FAILED
java.lang.IllegalStateException: Either class [com.example.xyz.Lagertyp] is not a domain class or GORM has not been initialized correctly or has already been shutdown. Ensure GORM is loaded and configured correctly before calling any methods on a GORM entity.
If I omit the validator, everything tests fine, but that's not what I want.
The domain class:
class Lagerort {
String lagerort
Lagertyp lagertyp
String beschreibung
static auditable = true
static constraints = {
lagerort(nullable: false, blank: false, unique: true)
lagertyp(nullable: false, blank: false, validator: { val, obj ->
// Only ONE Lagerort may be "Schrott"
if (Lagertyp.count() > 0) {
def _LAGERTYPSTRING="Schrott"
Lagertyp lagertypschrott = Lagertyp.findByLagertyp(_LAGERTYPSTRING)
if (obj.lagertyp == lagertypschrott && Lagerort.countByLagertyp(lagertypschrott)>0) return ['lagerortschrottunique',_LAGERTYPSTRING]
}
})
beschreibung(nullable: false, blank: false)
}
String toString(){lagerort}
}
The testCompile part of the dependencies in build.gradle looks like this:
testCompile "org.grails:grails-plugin-testing"
testCompile "org.grails.plugins:geb"
testCompile "org.grails.plugins:hibernate5"
testRuntime "org.seleniumhq.selenium:selenium-htmlunit-driver:2.47.1"
testRuntime "net.sourceforge.htmlunit:htmlunit:2.18"
I've already tried creating a few objects of type Lagertyp in the setup part of the controller tests so that Lagertyp.count() > 0 would be true for the validator, but that didn't help either.
The populateValidParams of the LagerortControllerSpec / test looks like this:
def populateValidParams(params) {
assert params != null
params["lagerort"] = 'Fa.Conrad'
params["lagertyp"] = ["lagertyp": 'Fa.Conrad', "beschreibung": 'Motor befindet sich bei Fa.Conrad']
params["beschreibung"] = 'in Reparatur bei Fa. Conrad'
}
The LagerortController: https://pastebin.com/PpZ5zqMm
The test for LagerortController: https://pastebin.com/pxZ6UeVK
Any ideas?

Found the solution, I had to also mock Lagertyp, like so:
#Mock([Lagerort,Lagertyp])
It seems that I have to include all domain classes which are part of the tests inside the #Mock list, even those that are referenced indirectly.

Related

Grails3 unit test for domain class with derived property

I have the following Domain class with derived property lowercaseTag.
class Hashtag {
String tag
String lowercaseTag
static mapping = {
lowercaseTag formula: 'lower(tag)'
}
}
If I run the following unit test, it will fail on the last line, because lowercaseTag property is null and by default all properties have nullable: false constraint.
#TestFor(Hashtag)
class HashtagSpec extends Specification {
void "Test that hashtag can not be null"() {
when: 'the hashtag is null'
def p = new Hashtag(tag: null)
then: 'validation should fail'
!p.validate()
when: 'the hashtag is not null'
p = new Hashtag(tag: 'notNullHashtag')
then: 'validation should pass'
p.validate()
}
}
The question is how to properly write unit tests in such cases? Thanks!
As I'm sure you've figured out, the lowercaseTag cannot be tested because it's database dependent; Grails unit tests do not use a database, so the formula/expression is not evaluated.
I think the best option is to modify the constraints so that lowercaseTag is nullable.
class Hashtag {
String tag
String lowercaseTag
static mapping = {
lowercaseTag formula: 'lower(tag)'
}
static constraints = {
lowercaseTag nullable: true
}
}
Otherwise, you'll have to modify the test to force lowercaseTag to contain some value so that validate() works.
p = new Hashtag(tag: 'notNullHashtag', lowercaseTag: 'foo')

how to unit test grails' message tag

in the controller there is an action:
def delete = {
withDomain {
it.delete()
flash.message = "${message(code: 'default.deleted.message', args: [message(code: 'chocolateBar.label', default: 'ChocolateBar'), it.name])}"
redirect action: 'list'
}
}
which can be tested in development. while in unit test, the message(..) method throws exception ( groovy.lang.MissingMethodException: No signature of method: longtest.ChocolateBarController.message() is applicable for argument types: (java.util.LinkedHashMap) values: [[code:chocolateBar.label, default:ChocolateBar]]):
public void testDelete() {
controller.params.id = '3'
controller.delete()
assert 'list'==controller.redirectArgs.action
}
After study, a mockTagLib method should be called during setup. But found no correct class name for built-in message(..). Please help.
I've solved the problem in unit controller test. like this:
//This is inside Spock test
#Shared
ResourceBundleMessageSource messageSource = null
#Shared
Closure mockMessage = {Map map ->
return messageSource.getMessage((String)map.code, (Object[])map.args, Locale.default)
}
def setupSpec(){
URL url = new File('grails-app/i18n').toURI().toURL()
messageSource = new ResourceBundleMessageSource()
messageSource.bundleClassLoader = new URLClassLoader(url)
messageSource.basename = 'messages'
messageSource.setDefaultEncoding("utf-8")
}
def setup(){
controller.metaClass.message = mockMessage
}
This code is for spock test, but main idea is also available for normal grails test.
In running phase(not test),
calling "message" in controller class results in calling "message" of ValidationTagLib class,
but they are not bind in unit test phase.
So I made almost same logic of "message" of ValidationTagLib,
and bind it(named "mockMessage") to controller.message.
With this code, you can execute "message" correctly in controller class in test.

Grails Unit Test doesn't fail

I am trying to perform basic unit test on a Grails domain class.
Here is the domain class:
class User {
String username
String password
String email
static constraints = {
username size: 4..15, blank: false, unique: true
password size: 5..15, password: true, blank: false
email email: true, blank: false
}
}
Here is the unit test class:
#TestFor(User)
class UserTests {
void testCreateUser() {
def u = new User(username:"ab")
assertFalse "There should be errors", u.validate()
assertTrue "Should be errors here", u.hasErrors()
}
}
username is constrained by size from 4 to 15. However, when I run grails test-app the above test succeeds. I don't understand why the constraint isn't causing it to fail.
You didn't write which Grails version you use, but generally you should set up User class to be tested for constraint checks. Add this to your UserTests
def setUp() {
mockForConstraintsTests(User)
}

Unit testing custom validator of command object with dependency

I have a command object for registering user, and I want to check how old is the user. This command object has a service dependency. How can I test custom validator for my dateOfBirth property? As it looks now is taken straight from documentation, here.
class RegisterUserCommand {
def someService
String username
String password
String password2
String email
Date dateOfBirth
static constraints = {
// other constraints
dateOfBirth blank: false, validator: {val, obj ->
return obj.someService.calculateAge(val) >= 18
}
}
So basically the question is: how can I mock 'obj' parameter of the validator closure?
The easiest way to test validation on a command object is to use GrailsUnitTestCase.mockForConstraintsTests. A mock validate method will be applied to your command object, and you can just call validate() like you would outside of a test.
Here's an example of how you could write your unit test. The blank constraint isn't meaningful for dates, so I've changed it to nullable: false.
import grails.test.GrailsUnitTestCase
class RegisterUserCommandTests extends GrailsUnitTestCase {
RegisterUserCommand cmd
protected void setUp() {
super.setUp()
cmd = new RegisterUserCommand()
mockForConstraintsTests RegisterUserCommand, [cmd]
}
void testConstraintsNull() {
cmd.dateOfBirth = null
cmd.someService = [calculateAge: { dob -> 18 }]
def result = cmd.validate()
assert result == false
assert cmd.errors.getFieldErrors('dateOfBirth').code == ['nullable']
}
void testConstraintsCustom() {
cmd.dateOfBirth = new Date()
cmd.someService = [calculateAge: { dob -> 17 }]
def result = cmd.validate()
assert result == false
assert cmd.errors.getFieldErrors('dateOfBirth').code == ['validator.invalid']
}
}
Note that your service won't get injected in a unit test (it will in an integration test though), so you'll either need to mock it, as above, or create an instance and assign it to cmd.someservice.

mocking command object in grails controller results in hasErrors() return false no matter what! Please help

I have a controller that uses a command object in a controller action. When mocking this command object in a grails' controller unit test, the hasErrors() method always returns false, even when I am purposefully violating its constraints. The more baffling thing is that in production, hasErrors() works! So this is just a testing problem.
def save = { RegistrationForm form ->
if(form.hasErrors()) {
// code block never gets executed
} else {
// code block always gets executed
}
}
In the test itself, I do this:
mockCommandObject(RegistrationForm)
def form = new RegistrationForm(emailAddress: "ken.bad#gmail",
password: "secret", confirmPassword: "wrong")
controller.save(form)
I am purposefully giving it a bad email address, and I am making sure the password and the confirmPassword properties are different. In this case, hasErrors() should return true... but it doesn't. I don't know how my testing can be any where reliable if such a basic thing does not work :/
Here is the RegistrationForm class, so you can see the constraints I am using:
class RegistrationForm {
def springSecurityService
String emailAddress
String password
String confirmPassword
String getEncryptedPassword() {
springSecurityService.encodePassword(password)
}
static constraints = {
emailAddress(blank: false, email: true)
password(blank: false, minSize:4, maxSize: 10)
confirmPassword(blank: false, validator: { confirmPassword, form ->
confirmPassword == form.password
})
}
}
Have you tried mockForConstraintsTests ?
E.g. something like...
void testSomething() {
mockForConstraintsTests(RegistrationForm)
def form = new RegistrationForm(emailAddress: "ken.bad#gmail", password: "secret", confirmPassword: "wrong")
form.validate()
assert 1 == form.errors.getErrorCount()
}
Try just testing the RegistrationForm command object first in its own unit test. (gain some confidence that its actually working)
Maybe using the above for the basis of your test will help!?!?