This is a simple domain class in a grails application:
class User {
String username
static constraints = {
username unique: true
}
}
My question is: should I write the unit test to check if a username field is unique?
#Test
void cannotCreateMoreThanOneUserWithTheSameUsername() {
new User(username: 'john').save()
def secondUser = new User(username: 'john')
assert !secondUser.validate()
}
I'm in doubt because:
If I write the User class in accordance with TDD principles then I should write failing test before implementing constraints closure.
On the other hand, setting an unique constraint in domain is rather a data model configuration than a true logic. And what's more, the save and validate methods are implemented in the framework.
In my opinion, unit testing CRUD methods is not worth the time since the Grails devs have fully tested these already. Unit testing constraints, on the other hand, is important because constraints can change during the lifecycle of your app and you want to make sure you're catching those changes. You never know what business logic might need to be modified to support said changes. I like to use Spock for this and a typical constraint test would look something like this:
#TestFor(User)
class UserSpec extends ConstraintUnitSpec {
def setup() {
mockForConstraintsTests(User, [new User(username: 'username', emailAddress: 'email#email.com')])
}
#Unroll("test user all constraints #field is #error")
def "test user all constraints"() {
when:
def obj = new User("$field": val)
then:
validateConstraints(obj, field, error)
where:
error | field | val
'blank' | 'username' | ' '
'nullable' | 'username' | null
'unique' | 'username' | 'username'
'blank' | 'password' | ' '
'nullable' | 'password' | null
'maxSize' | 'password' | getLongString(65)
'email' | 'emailAddress' | getEmail(false)
'unique' | 'emailAddress' | 'email#email.com'
'blank' | 'firstName' | ' '
'nullable' | 'firstName' | null
'maxSize' | 'firstName' | getLongString(51)
'blank' | 'lastName' | ' '
'nullable' | 'lastName' | null
'maxSize' | 'lastName' | getLongString(151)
'nullable' | 'certificationStatus' | null
}
}
Here's the ConstraintUnitSpec base class:
abstract class ConstraintUnitSpec extends Specification {
String getLongString(Integer length) {
'a' * length
}
String getEmail(Boolean valid) {
valid ? "test#wbr.com" : "test#w"
}
String getUrl(Boolean valid) {
valid ? "http://www.google.com" : "http:/ww.helloworld.com"
}
String getCreditCard(Boolean valid) {
valid ? "4111111111111111" : "41014"
}
void validateConstraints(obj, field, error) {
def validated = obj.validate()
if (error && error != 'valid') {
assert !validated
assert obj.errors[field]
assert error == obj.errors[field]
} else {
assert !obj.errors[field]
}
}
}
This is a technique I learned from a blog post. But I can't recall it right now. I'll hunt for it and if I find it, I'll be sure and link to it.
I would concentrate your testing effort on areas that are likely to go wrong, rather than trying to obtain 100% coverage.
With this in mind, I avoid testing anything that is simply declared. There is no logic for you to break and any test would just be repeating the declaration. It's hard to see how this would save you from accidently breaking this functionality.
If you were coding the underlying library that deals with the declaration, then you should be writnig tests. If not, rely on the library. Of course, if you didn't trust the library authors to get this right, then you could write tests. There is a trade-off here of testing effort vs reward.
After some more research I want to share a next test sample for the same User class and finally answer my own question.
#Test
void usernameIsUnique() {
def mock = new ConstraintsMock()
User.constraints.delegate = mock
User.constraints.call()
assert mock.recordedUsernameUniqueConstraint == true
}
class ConstraintsMock {
Boolean recordedUsernameUniqueConstraint = null
def username = { Map m ->
recordedUsernameUniqueConstraint = m.unique
assert m.unique
}
}
This is very naive test sample. It is rather a test of implementation than a behaviour, what in my opinion is bad.
But does it really differ from a test sample in the question?
First things first: what logic do we want to test? What is the the true logic of constraints closure? It's just calling a gorm's dynamic methods for each field we want to configure and passing a configuration as a parameter.
So why not just call this closure in test? Why would I call save method? Why would I call gorm's validate method? From this point of view, directly calling constraints closure in unit tests seems to be not so bad idea.
On the other hand, what is a difference between constraints closure and configuration closures in Config.groovy? We do not test a configuration, do we?
I think we don't test a configuration because the test of configuration would be just like a carbon copy of this configuration (repeating ourselves).
What's more, this kind of test won't even increase code coverage, if somebody still cares about this metrics today, because first run of integration or functional test should run all constraints of all domains.
The last thing: what kind of bug this test is able to catch in a real life?
To summarize: in my opinion setting up the simple constraints like "blank", "nullable" or unique is very similar to the application configuration. We should not test this part of code because if a such test is something more than carbon copy of our constraints definition it propably checks only a framework's logic.
I wrote many unit tests for constraints. Now I'm removing them back during a refactoring. I will leave only unit tests of my own validators logic.
Related
I am trying to test some methods within a Domain object, the code seems to execute (based on log) but the assertions fail.
The code being tested (extendDates), is working, I am just adding the unit tests now.
I assume I am doing something wrong in the mocking.The following is a simplified version of my code. Based on the log output, the assertion should pass.
class EventDate{
Date startDate
Date endDate
belongsTo = [Appointments owner]
static constraints = {
endDate(nullable:true, blank:false)
startDate(nullable:false, blank:false)
}
}
class Appointments {
hasMany = [ eventDates: EventDate]
belongsTo = [ customer: Customer ]
def extendDates(start,end){
//some logic on eventDates...
EventDate(startDate:start,endDate:end, owner:this).save(flush:true,failOnError:true);
}
}
#TestFor(Appointments)
#Mock([EventDate])
class AppointmentsTests {
void testDateExtend(){
assertTrue domain != null
assertTrue domain instanceof Appointments
//Log indicates the correct execution and creation of event
domain.extendDates(new Date(),null)
//following returns NullPointerException
assertTrue domain.eventDates.size() == 1
}
}
In your example you test for
if (create_new)
The variable "create_new" is never set and would therefore test false using groovy truth logic, thus never executing the if statement.
The if statement never adds anything to the "eventDates" property of Appointments, this would also mean that the assertion would fail.
I'm thinking your example is incomplete and therefore cannot help you until you expand it.
Yes, you will get NullPointerException in the assert condition. The reason being that, you are creating instance of EventDate in the extendDates method but you are not really adding it to the eventDates list in Appointments domain.
So, you have to modify your that method something like:
// Initialize with empty array to avoid NPE
List<EventDate> eventDates = []
static hasMany = [ eventDates: EventDate]
def extendDates(start, end) {
EventDate instance = EventDate(start, end).save()
// And add it to the list of events
this.addToEventDates(instance)
this.save(flush: true)
}
Now, your test case should work your assert condition.
(Also, looks like you have not added nullable constraint in end but passing the null value while creating instance of EventDate, may be not included in sample code)
I have been trying to figure this out for 2 days now and I am really stuck and frustrated. I have a domain object with a service which is being used for custom validation. The domain looks like this:
class Llama {
String name
transient myFetcherService
static transients = [
'myFetcherService'
]
static constraints = {
name validator: { val, obj ->
if (obj.nameExists(val) == true) {
//return some error here.
}
}
}
protected boolean nameExists(String name) {
List<Llama> llamasList = myFetcherService.fetchExistingLlamasByName(name)
if (llamasList.isEmpty()) {
return false
}
return true
}
}
Now, I have another Service, which simply saves a list of Llama objects. It looks like this:
class LlamaFactoryService {
public void createLlamas(List<String> llamaNames) {
llamaNames.each { name ->
new Llama(name: name).save()
}
}
}
In my test. I keep getting this error:
Failure: createLlamas should create Llammas (com.myLlamaProject.LlamaFactoryServiceSpec)
| java.lang.NullPointerException: Cannot invoke method myFetcherService on null object
I don't understand. In my tests, added a metaClass for the service in the "given" section. When it tries to save, it's telling that the service is null. This is what my test looks like:
given:
def myFetcherService = mockFor(MyFetcherService)
myFetcherService.demand.fetchExistingLlamasByName {def name -> return []}
Llama.metaClass.myFetcherService = myFetcherService.createMock()
when:
service.createLlamas(['Pablo','Juan','Carlos'])
then:
//some validations here....
I also tried using metaClass on the method nameExists() like:
Llama.metaClass.myFetcherService = { def name -> false }
, but it gives me the same nullPointerException as the one above. Could someone point me to the right direction? I'm a bit stuck. :(
Thanks in advance for reading and helping.
You're using a unit test and the general rule for unit tests is that beans generally aren't created for you, so you'll need to inject them yourself.
(Code edited to reflect the fact I misread the question)
I think you want a testing pattern something like:
given:
def mockMyFetcherService = Mock(MyFetcherService) // create the mock
Llama.metaClass.getMyFetcherService = { mockMyFetcherService } // inject the dependency
def returnList = [] // let's just define this here and you can re-use this pattern in other tests more easily
when:
service.createLlamas(['Pablo','Juan','Carlos'])
then:
// tell Spock what you expect to have happen with your Mock - return the List you defined above
3 * mockFetcherService.fetchExistingLlamasByName(_) >> returnList
If the injection of the service into the metaClass doesn't work (suggested here), you could always try using the defineBeans{} closure within the unit test itself (http://www.block-consult.com/blog/2011/08/17/inject-spring-security-service-into-domain-class-for-controller-unit-testing/).
Thus you could try:
defineBeans {
myFetcherService(MockMyFetcherService)
}
where MockMyFetcherService is defined in the same file that defines the test. This is the approach followed here:
See here for examples of more Spock interactions.
If you're using Grails 2.4.3 or below you'll need to put CGLIB in BuildConfig.groovy but I see here that it's already done for you in 2.4.4, so you should be ok just to use Mock(classname).
I'm using Grails 2.4.1 and having trouble understanding how to properly test a unique constraint on a domain class.
My domain class looks like:
class Person {
String name
static constraints = {
name( unique: true, blank: false )
}
}
and my test:
#TestFor(Person)
#TestMixin(GrailsUnitTestMixin)
class PersonSpec extends Specification {
void "name should be unique"() {
given:
mockForConstraintsTests Person
when:
def one = new Person( name: 'john' )
then:
one.save()
when:
def two = new Person( name: 'john' )
then:
! two.validate()
two.errors.hasFieldErrors( 'name' )
}
The second instance is validating despite apparently violating the unique constraint. Can someone please explain what's going on here and how I should best correct it?
Thanks!
--john
I do not think it is the best approach to test the constraints by triggering them. Generally we want to test that our code is working and not that Grails is validating the constraints.
Why test the framework (i.e. Grails)? Hopefully the Grails people have done that already :-)
(yes, there are situations were it makes sense to check the framework, but I don't think this is one).
So the only thing to test is that we have placed the correct constraints on the domain class:
#TestFor(Person)
class PersonSpec extends Specification {
void "name should have unique/blank constraints"() {
expect:
Person.constraints.name.getAppliedConstraint('unique').parameter
! Person.constraints.name.getAppliedConstraint('blank').blank
}
}
If it is worth writing that test is another question...
This is exactly kind of mistake I used to do as a beginner. Testing grails stuff whether it is working as expected or not. Testing framework related stuff not a good idea. It means then testing all constraints, predefined methods and even save(), update() etc. So it would mean testing grails framework again rather than developing your application.
Testing should generally consists of testing your business logic related stuff.
I am currently trying to Unit Test a domain model's constraints in Grails using the Spock framework and I am having some issues. So I have the following domain model with the various constraints:
class profile {
String phoneNo
String userName
static belongsTo = [group:Group]
static constraints = {
phoneNo(blank:false, maxsize:14, matches:"44[0-9]{10}")
}
}
Then I have this test that should be able to test each of the field constraints one at a time and return the expected results:
#Unroll("test profile all constraints #field is #error")
def "test profile all constraints"() {
when:
def newObj = new Profile("$field": val)
then:
validateConstraints(newObj, field, error)
where:
error | field | val
'blank' | 'phoneNo' | '447897654321'
'maxSize' | 'phoneNo' | '123456789012'
'matches' | 'phoneNo' | getPhoneNumber(true)
}
However when I run the test say for the Phone Number Field's Max Size constraint and pass it a value smaller than the max size available I would expect this test to pass but it fails, in fact all of the tests fail and I am unsure why as I am new to using this framework. I would really appreciate some help on this.
Thanks in advance
I have now managed to fix this issue.
The issue was related to the mocking of the constraints, I was mocking up the wrong constraints for the test I wanted to do.
Thanks for the help
I have the following as my unit test:
void testCreateDealer() {
mockForConstraintsTests(Dealer)
def _dealer= new Dealer( dealerName:"ABC",
Email:"abc-motors#global.com",
HeadOffice:"",
isBranch:false)
assertFalse _dealer.validate()
}
But when I run the test I get the following error:
No signature of method: static com.myCompany.Dealer.findByDealerNameIlike() is applicable for argument types: (java.lang.String) values: [ABC]
I use some custom constraints in my domain class. How Can I test this?
static constraints = {
dealerName(blank:false, validator:
{ val, obj ->
def similarDealer = Dealer.findByDealerNameIlike(val)
return !similarDealer || (obj.id == similarDealer.id)
}
)
Try changing mockForConstraintsTests() to mockDomain() - you're using a Dealer.findX() method in the constraint, which relies on the Dealer domain.
Incidentally, the test will still fail unless you've created a similar dealer in the setUp() method of the test class.
In unit tests, even with mockDomain, the id attribute of domain objects is not set automatically, or auto-incremented. All of the domain objects you create will have an id of null unless you explicitly set it.
Your test is probably failing because the test obj.id == similarDealer.id is true, since they both have id: null. Try setting the id attribute of your mocked dealer objects.