grails, unit test mock domain with assigned id - unit-testing

I am using assigned id in my domain
class Book {
Integer id
String name
static mapping = {
id generator: 'assigned'
}
}
so to add a new book:
def book = new Book([name: "The Adventures of Huckleberry Finn"])
book.id = 123
book.save(flush: true)
everything works perfectly, the problem is in my unit tests
first of all I can only mock 1 domain class,
secondly, I cannot use .save() on unit test, so my only option (as far as i know) is to use mockDomain as follow:
mockDomain(Book, [ [id: 123, name: "The Adventures of Huckleberry Finn"] ])
but it is not working, it would work in a normal domain without "id generator: 'assigned'"
any ideas?
I read that I wouldn't face this problem in integrated test, it is just a problem in unit test
thanks

You would need the bindable constraint for id if you want to use (by default id is not bindable) it as map params to create the domain object in unit test. The domain class would have
static constraints = {
id bindable: true
}
Words of advice:
If you are using Grails > 2.x, use #Mock to mock domain classes instead of mockDomain. You can find details about Unit Testing in Grails docs.
Another Level Up
Use build-test-data plugin to mock domain objects.

This solution fits my needs:
Book mockBook = [name: "The Adventures of Huckleberry Finn"] as Book
mockBook.metaClass.id = 123
assert mockBook.id == 123

Related

unit testing the unique constraint on domain class

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.

Grails unit tests not recognizing .save() on a domain class

I am trying to be a good little programmer and set up Unit tests for my Grails 2.2.3 app. The unit tests that use GORM's injected .save() method are apparently not persisting to the mock test DB. For an example, here is what one test consists of:
#TestFor(TermService)
#Mock(Term)
class TermServiceTests {
void testTermCount() {
def t = new Term(code: "201310").save(validate: false, flush: true, failOnError: true)
println "Printing Term: " + t.toString()
assert 1 == Term.count() // FAILS
assert service.isMainTerm(t) // FAILS
}
}
I did a println that ends up printing Printing Term: null, meaning the Term did not save and return a Term instance. The first assertion is false with Term.count() returning 0.
Does anyone know why this might be? I have a mock Term and TermService (via the TestFor annotation, I believe), so I'm not quite sure why this wouldn't work. Thanks!
Edit: Here is my Term class.
class Term {
Integer id
String code
String description
Date startDate
Date endDate
static mapping = {
// Legacy database mapping
}
static constraints = {
id blank: false
code maxSize: 6
description maxSize: 30
startDate()
endDate()
}
}
Looks like id generator is assigned since you have mentioned about using legacy database. Plus id is not bindable by default in domain class (map construct won't work for id). So, I think you have to end up using like below:
def t = new Term(code: "201310")
t.id = 1
t.save(...)

Mocking the "save" method on domain classes

I'm having hard time mocking the save instance method in my unit tests in Grails 1.3.3. I've created a simple domain class named Person, it has one property (nullable) called "name".
package tutorial
class Person {
String name
static constraints = {
name nullable: true
}
}
In my test I'm trying to do something I've found in the documentation:
class PersonTests extends GrailsUnitTestCase {
public void testCanSavePerson() {
def testInstances = []
mockDomain(Person, testInstances)
assertEquals(0, Person.count())
new Person(name: "Bob").save()
assertEquals(1, Person.count())
}
}
However as soon as I run the test what I get is an exception:
java.lang.NullPointerException
at grails.test.MockUtils$_addValidateMethod_closure83.doCall(MockUtils.groovy:973)
at grails.test.MockUtils$_addValidateMethod_closure84.doCall(MockUtils.groovy:1014)
at grails.test.MockUtils$_addDynamicInstanceMethods_closure67.doCall(MockUtils.groovy:736)
at grails.test.MockUtils$_addDynamicInstanceMethods_closure67.doCall(MockUtils.groovy)
at tutorial.PersonTests.testCanSavePerson(PersonTests.groovy:25)
whereas the line 25 is exactly the one that calls save() on newly created instance.
Does anyone knows what am I doing wrong?
This is an already known bug in Grails 1.3.3. Read more about it and find a workaround in the associated JIRA ticket GRAILS-6482.

Testing custom constraints in Grails App

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.

Subsonic 3 ActiveRecord Setup() behavior

Been a long time user of Subsonic 2.x and have used 3.x a bit, but I've recently started transitioning our use of SS from using repositories to ActiveRecord instead. I'm currently stumbling on some of our unit tests and I wonder if it's perhaps because I'm misunderstanding the intent of the Setup() method. Alas, the only documentation I can find is on Rob Conery's blog.
On my unit tests, I'm stuffing a collection of objects, let's say a List of Accounts. I then want to validate that some code is properly filtering against the repo by a property, let's say the email address. My (simplified) unit test setup is below.
The kicker is that when using the "Test" connection strings, it seems like any LINQ I write against the repo returns me all the records I stuffed into the Setup--which is making me wonder if I'm misunderstanding the intention of Setup(). It's as if it were behaving like a Mock setup, e.g. mymock.Setup(foo => foo.Email).Returns("user#user.com").
List accounts = new List()
{
new Account() { FirstName = "Paul", LastName = "McCartney", Email = "paul#beatles.com" },
new Account() { FirstName = "John", LastName = "Lennon", Email = "john#beatles.com" },
new Account() { FirstName = "Ringo", LastName = "Starr", Email = "ringo#beatles.com" },
new Account() { FirstName = "George", LastName = "Harrison", Email = "george#beatles.com" },
new Account() { FirstName = "Taylor", LastName = "Swift", Email = "immaletyou#finish.com" }
};
DB.Account.ResetTestRepo();
DB.Account.Setup( accounts );
Elsewhere, the code I'm trying to unit test is basically performing a Find(). The real implementation has a semi complex set of conditions, but even simplified conditions don't appear to work.
Account.Find(a => a.Email == "immaletyou#finish.com").SingleOrDefault();
The above will bomb with an exception indicating that the lambda returned multiple elements. When I debug into the test, sure enough, the result of the Find() is all the objects I had stuffed into the mocked repo via the Setup() method.
Rob C laments that ActiveRecord can be tough to test--which is a bummer. But I can't imagine that the testing scenario is breaking on such a mundane sample--it's PEBKAC right?
Halp!?
Edit:
Josh Rivers asks what appears to be a similar question, though it doesn't appear to be resolved. Linking for completeness.
Going to answer my own question for any future parties, not that there's a ground swell of activity rushing to this thread:
Turns out that the current implementation of test repositories (Subsonic 3.03) has a bug where it basically just returns the entire set of values inside the repository. The current fix (haven't tested myself, but has worked for others) is to pull the current main line of the source code and recompile.
See: Subsonic Issue 109