Grails unit testing working in different way - unit-testing

I am using grails 2.3.6 version . I have two domain class as below
class Profile {
String name
static belongsTo = Student
}
class Student {
Profile profile
Integer enrolmentNumber
}
I am trying to create one to one unidirectional relationship . The problem is I have written a unit test case
#TestFor(Student)
#Mock([Profile,Student])
class StudentSpec extends Specification {
void "test save Student"() {
when :"Student details"
Profile profile = new Profile(name:"Test")
Student student = new Student(enrolmentNumber: 10,profile:profile)
then : " Student should be saved"
assertNotNull(student.save(flush: true))
assertNotNull(profile.id)
when:"When I delete the student"
student.delete()
then:"Profile should also deleted"
assertNull Profile.findById(profile.id)
}
}
test is failing at the last line "Profile.findById(profile.id)" saying profile is found. As per my understanding when I have "belongsTo" then cascading should happen ,I mean when I delete the Student, Profile should also be delete . But its not happening .
Instead of creating a unit test , I used the same code in Integration test , its working correctly.
Not Sure what I am doing wrong. Or my understanding is wrong.

Try
student.delete(flush:true)

Related

Odd behavior with default index action when unit testing a RestfulController in grails

Using grails 2.4.4 (and 2.4.5) I created the following tiny application.
grails create-app example
cd example
grails create-domain-class Book
Then edited Book.groovy to look like
package example
class Book {
String title;
static constraints = {
}
}
Then added a basic Controller
grails> create-controller example.book
| Created file grails-app/controllers/example/BookController.groovy
| Created file grails-app/views/book
| Created file test/unit/example/BookControllerSpec.groovy
and modify the controller to extend RestfulController.
package example
import grails.rest.*
class BookController extends RestfulController<Book> {
static responseFormats=['json','xml']
BookController() {
super(Book)
}
}
Hook up the UrlMappings to serve up the books as a resource at /api/books.
class UrlMappings {
static mappings = {
"/api/books"(resources: "book")
"/"(view:"/index")
"500"(view:'/error')
}
}
and last but not least, built a few books in BootStrap.groovy.
import example.*
class BootStrap {
def init = { servletContext ->
new Book(title: "Book 1").save(failOnError:true);
new Book(title: "Book 2").save(failOnError:true);
new Book(title: "Book 3").save(failOnError:true);
}
def destroy = {
}
}
then grails run-app
And everything looks good. The example.Book controller shows up in the index page. The three books can be viewed as json or xml.
So now to the unit test.
Edit the BookControllerSpec to look like this
package example
import grails.test.mixin.TestFor
import grails.test.mixin.Mock
import spock.lang.Specification
#TestFor(BookController)
#Mock(Book)
class BookControllerSpec extends Specification {
def setup() {
new Book(title: "Unit Test Book 1").save(failOnError:true);
new Book(title: "Unit Test Book 2").save(failOnError:true);
new Book(title: "Unit Test Book 3").save(failOnError:true);
}
void "My Setup Worked"() {
given: "My setup ran"
when: "I ask for the count"
then: "I should get back 3"
Book.count() == 3;
}
void "I can access my Controller index method"() {
given: "My setup ran"
when: "I call index"
request.method="GET"
response.format="xml"
controller.index();
then: "I get an XML object back with 3 books"
println response.contentAsString
response.xml.book*.title.size()==3
}
}
The first test passes, and the 2nd one fails. The output from the println in the failing test is an empty xml list of books.
<?xml version="1.0" encoding="UTF-8"?><list />
While investigating what was going on I looked at the index method of the parent class (RestfulController).
By copying that method into BookController, the unit test will start passing.
-- new version of BookController with copied index method --
package example
import grails.rest.*
class BookController extends RestfulController<Book> {
static responseFormats=['json','xml']
BookController() {
super(Book)
}
def index(Integer max) {
params.max = Math.min(max ?: 10, 100)
respond listAllResources(params), model: [("${resourceName}Count".toString()): countResources()]
}
}
So is there anything else I need to add to the Spec in order to get the index method of the RestfulController to work without having to copy the index method into the BookController?
Any idea why the call to listAllResources works when executed directly from BookController, but returns no rows when executed from the RestfulController.
The unit test above is a modified version of the rest unit tests described in the Grails In Action book, which is written for grails 2.3. http://www.manning.com/gsmith2/
Unit tests can get really tricky with grails. I never determined exactly what causes this failure, but I did discover another oddity.
Inserting the failing test into the spec file twice will cause it to fail the first time, but pass the 2nd time.
void "first call to index doesn't work."() {
given: "My setup ran"
when: "I call index"
request.method="GET"
response.format="xml"
controller.index();
then: "I get an XML object back with 3 books"
println response.contentAsString
response.xml.book*.title.size()==3
}
void "2nd call to index works"() {
given: "My setup ran"
when: "I call index"
request.method="GET"
response.format="xml"
controller.index();
then: "I get an XML object back with 3 books"
println response.contentAsString
response.xml.book*.title.size()==3
}
Something deep in the bowels of the grails testing framework doesn't successfully hook up that index method the first time it is called.
Instead of digging any further, I just rewrote this as an integration test and it started behaving properly.
There is a lot of magic you get for free with grails unit testing, but when the magic doesn't work it's probably better to try a different testing phase.

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 test mock domain with assigned id

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

SDN 2.1.0/neo4j 1.8: java.lang.IllegalArgumentException: Cannot obtain single field value for field 'schoolRef'

I am able add and save multiple SchoolRef, but am getting the error after retrieving the (ancestor and eagerly fetching the) Education object and then attempting to add another SchoolRef. This was working with SDN 2.0.1, but I've also changed other things, including the Repository/Cypher query below, so I can't isolate it to the upgrade.
#Fetch #RelatedTo(type = "EDUCATION_HAS_SCHOOLREF")
private Set<SchoolRef> schoolRefs = new HashSet<SchoolRef>();
public Education() {
}
public void addSchoolRef(SchoolRef schoolRef) {
getSchoolRefs().add(schoolRef);
}
Repository:
public interface UserRepository extends GraphRepository<User>, CypherDslRepository<User> {
#Query("start id=node:Identifier(identifier={0}) match id<-[:USER_HAS_IDENTIFIER]-user return user")
public User findById(String id);
Stacktrace:
Caused by: java.lang.IllegalArgumentException: Cannot obtain single field value for field 'schoolRef'
at org.springframework.data.neo4j.fieldaccess.RelatedToSingleFieldAccessorFactory$RelatedToSingleFieldAccessor.getValue(RelatedToSingleFieldAccessorFactory.java:94)
at org.springframework.data.neo4j.fieldaccess.DefaultEntityState.getValue(DefaultEntityState.java:97)
at org.springframework.data.neo4j.support.mapping.SourceStateTransmitter.copyEntityStatePropertyValue(SourceStateTransmitter.java:90)
at org.springframework.data.neo4j.support.mapping.SourceStateTransmitter.access$000(SourceStateTransmitter.java:40)
at org.springframework.data.neo4j.support.mapping.SourceStateTransmitter$2.doWithAssociation(SourceStateTransmitter.java:61)
at org.springframework.data.mapping.model.BasicPersistentEntity.doWithAssociations(BasicPersistentEntity.java:207)
at org.springframework.data.neo4j.support.mapping.SourceStateTransmitter.copyPropertiesFrom(SourceStateTransmitter.java:57)
at org.springframework.data.neo4j.support.mapping.Neo4jEntityConverterImpl.loadEntity(Neo4jEntityConverterImpl.java:100)
at org.springframework.data.neo4j.support.mapping.Neo4jEntityConverterImpl.read(Neo4jEntityConverterImpl.java:92)
at org.springframework.data.neo4j.support.mapping.Neo4jEntityPersister$CachedConverter.read(Neo4jEntityPersister.java:170)
at org.springframework.data.neo4j.support.mapping.Neo4jEntityPersister.createEntityFromState(Neo4jEntityPersister.java:189)
at org.springframework.data.neo4j.support.Neo4jTemplate.createEntityFromState(Neo4jTemplate.java:180)
at org.springframework.data.neo4j.fieldaccess.RelationshipHelper.createEntitySetFromRelationshipEndNodes(RelationshipHelper.java:130)
at org.springframework.data.neo4j.fieldaccess.RelatedToFieldAccessor.createEntitySetFromRelationshipEndNodes(RelatedToFieldAccessor.java:86)
at org.springframework.data.neo4j.fieldaccess.RelatedToSingleFieldAccessorFactory$RelatedToSingleFieldAccessor.getValue(RelatedToSingleFieldAccessorFactory.java:76)
at org.springframework.data.neo4j.fieldaccess.DefaultEntityState.getValue(DefaultEntityState.java:97)
at org.springframework.data.neo4j.support.mapping.SourceStateTransmitter.copyEntityStatePropertyValue(SourceStateTransmitter.java:90)
at org.springframework.data.neo4j.support.mapping.SourceStateTransmitter.access$000(SourceStateTransmitter.java:40)
at org.springframework.data.neo4j.support.mapping.SourceStateTransmitter$2.doWithAssociation(SourceStateTransmitter.java:61)
at org.springframework.data.mapping.model.BasicPersistentEntity.doWithAssociations(BasicPersistentEntity.java:207)
at org.springframework.data.neo4j.support.mapping.SourceStateTransmitter.copyPropertiesFrom(SourceStateTransmitter.java:57)
at org.springframework.data.neo4j.support.mapping.Neo4jEntityConverterImpl.loadEntity(Neo4jEntityConverterImpl.java:100)
at org.springframework.data.neo4j.support.mapping.Neo4jEntityConverterImpl.read(Neo4jEntityConverterImpl.java:92)
at org.springframework.data.neo4j.support.mapping.Neo4jEntityPersister$CachedConverter.read(Neo4jEntityPersister.java:170)
at org.springframework.data.neo4j.support.mapping.Neo4jEntityPersister.createEntityFromState(Neo4jEntityPersister.java:189)
at org.springframework.data.neo4j.support.mapping.Neo4jEntityPersister.persist(Neo4jEntityPersister.java:244)
at org.springframework.data.neo4j.support.mapping.Neo4jEntityPersister.persist(Neo4jEntityPersister.java:231)
at org.springframework.data.neo4j.support.Neo4jTemplate.save(Neo4jTemplate.java:293)
at org.springframework.data.neo4j.support.Neo4jTemplate.save(Neo4jTemplate.java:287)
at org.springframework.data.neo4j.fieldaccess.RelationshipHelper.getOrCreateState(RelationshipHelper.java:119)
at org.springframework.data.neo4j.fieldaccess.RelationshipHelper.createSetOfTargetNodes(RelationshipHelper.java:111)
at org.springframework.data.neo4j.fieldaccess.RelatedToFieldAccessor.createSetOfTargetNodes(RelatedToFieldAccessor.java:82)
at org.springframework.data.neo4j.fieldaccess.RelatedToCollectionFieldAccessorFactory$RelatedToCollectionFieldAccessor.setValue(RelatedToCollectionFieldAccessorFactory.java:66)
at org.springframework.data.neo4j.fieldaccess.ManagedFieldAccessorSet.updateValue(ManagedFieldAccessorSet.java:94)
at org.springframework.data.neo4j.fieldaccess.ManagedFieldAccessorSet.update(ManagedFieldAccessorSet.java:82)
at org.springframework.data.neo4j.fieldaccess.ManagedFieldAccessorSet.add(ManagedFieldAccessorSet.java:108)
---- Edit:
Same error, but under different circumstances..
School school = new School();
school = neo4j.repositoryFor(School.class).save(school);
User user1 = new User("Junit", "1");
SchoolRef schoolRef1 = new SchoolRef();
schoolRef1.setSchool(school);
user1.addSchoolRef(schoolRef1);
user1 = neo4j.repositoryFor(User.class).save(user1);
User user2 = new User("Junit", "2");
SchoolRef schoolRef2 = new SchoolRef();
schoolRef2.setSchool(school);
user2.addSchoolRef(schoolRef2);
user2 = neo4j.repositoryFor(User.class).save(user2); // <- error here
sometimes I can be blind to the obvious problem...
In my case, SchoolRef references a single school, but schools can have many schoolRefs. I had incorrectly implemented School with a single reference back to SchoolRef.
I was able to create multiple SchoolRefs which referenced a single School, but got this error when I tried to fetch a School which had the multiple references.
We ran into this issue as well, but it was due to having a separate relationship with the same label.

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.