Grails Test Domain Model - Mocks - unit-testing

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)

Related

Grails Spock Mocking an Object

I am new in unit-testing in Grails application using Spock. However I would like to ask the following question. Lets say I want to run a test for the following function testfun.
class TestFun{
boolean testfun(long userId, long orderId){
User user = User.findByUserId(userId)
if(user == null)return false
Order order = Order.findByUserAndId(user, orderId)
HashMap<String, Object> = orderContent
orderContent= order.orderContent // the order has an element orderContent for storing the elements that one user orders
if(orderContent!=null){
orderContent.put("meal",1)
order.orderContent = orderContent
return true
}
return false
}
}
The corresponding unit test in that case would be:
class TestFun extends Specification {
def setup() {
GroovySpy(User, global: true)
GroovySpy(Order, global: true)
}
def "test funtest"() {
User user = new User(2).save()
Order order = new Order(3).save()
when:
service.testfun(2,3) == result
then:
2*User.findByUserId(2) >> Mock(User)
1*Order.findByUserAndId(_ as User, 1)>> Mock(Order)
result == true
}
}
However, I think that I have to mock the order.orderContent and I do not know how to mock it. Right now the test fails, because the orderContent is null so the testfun returns false.
Can anyone help me on that?
There are several things going on here, and hopefully fixing them will help you get the test running and passing.
I can't recall for certain, but I believe GroovySpy is an old feature, that isn't used with Spock tests. Instead of using that to mock a domain class, you should be using the #Mock annotation before the class definition, to specify which domain classes you would like to mock.
While you can mock the domain classes, you'll also need to actually populate the in-memory database with those objects, either in a setup: block, or in a setup() method, if they are needed for multiple tests.
You mention creating mocks, and using the Spock Mock() method will create a mock of that object, but you don't want to use that for domain objects. It is more typically used for service classes that will be manually injected into your test class.
When saving a mock domain class, I would suggest including the parameters flush: true, failOnError: true, that way any validation that fails will be indicated immediately and on the appropriate line. Without this, you can get some strange errors to occur later in the tests.
I don't know what you're doing in the when: block, but you should not be doing an assertion with == at that point, do all of those in the then: block.
Given all of this, I think you test class should look more like this:
#Mock([User, Order])
class TestFun extends Specification {
def setup() {
// This object will have an id of 1, if you want a different id, you can either specify it, or create more objects
User user = new User(first: "john", last: "doe").save(flush: true, failOnError: true)
new Order(user: user).save(flush: true, failOnError: true)
}
def "test funtest"() {
User user = new User(2).save()
Order order = new Order(3).save()
when:
boolean result = service.testfun(1, 1)
then:
// Optionally you can just use "result" on the next line, as true will be assumed
result == true
}
}

Grails 2.4.4 : How do mock a transient service inside a domain?

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).

How do I mock local variables of a mocked class in a Spock test?

Let's say I have the following method in a service:
private void deleteItems(List<Item> itemsToDelete) {
def sql = new Sql(dataSource)
itemsToDelete?.each { Item item ->
sql.execute("DELETE FROM owner_item WHERE item_id = ${item.id}")
item.delete(flush: true, failOnError: true)
flushDatabaseSession();
}
}
How do I create a test for this method in the ItemServiceSpec? When I try it, I get either a DataSource "Must specify a non-null Connection" error or a nullPointerException on sql.
This is my existing test.
#TestFor(ItemService)
#Mock([Item])
#Build([Item])
class SubjectServiceSpec extends Specification {
...
def "delete items"() {
given:
Item item1 = Item.build().save(flush: true)
Item item2 = Item.build().save(flush: true)
Item.count() == 2
DataSource mockDataSource = Mock()
service.dataSource = mockDataSource
1 * deleteItems
when:
service.deleteItems([item1, item2])
then:
Item.count() == 0
}
}
What you are trying to do here, is to mock a dependency (DataSource) of a dependency (Sql). This normally leads to a situation, where you a not 100% aware of how the Sql interacts with the DataSource Object. If Sql changes private interaction with the Datasource in a Version Update, you have to deal with the situation.
Instead of mocking a dependency of a dependency you should the Sql Class directly. For this, the sql has to be some kind of explicit dependency that you can receive via DI or a method parameter. In this case you can just mock the execute call like so (choosen the way of a Expando-Mock, but you could also use Map or the Mock Stuff from Spock):
given:
def sqlMock = new Expando()
sqlMock.execute = { return 'what ever you want or nothing, because you mock a delete operation' }
service.sql = sqlMock
when:
service.deleteItems([item1, item2])
then:
assertItemsAreDeletedAndTheOwnerAsWell()
Thinking about the whole testcase, there a two major problems in my opinion.
The first one is, when you ask yourself what kind of certainty do you really get here by mocking out the whole sql stuff. In this case, the only thing that you are doing here is to interact with the db. When you mock this thing out, then there is nothing anymore that you could test. There is not many conditional stuff or anything that should be backed up by a unit test. Due to this, I would suggest to write only integration spec for this test-case where you have something like a H2DB for testing purposes inplace.
The second thing is, that you actually don't need the Sql Manipulation at all. You can configure GORM and Hibernate in a way do a automatic and transparent deletion of the owner of the item, if the item is deleted. For this, look at the docs (especially the cascade part) from GORM or directly in the Hibernate docs.
To sum it up: use cascade: 'delete' together with a proper integration test and you have a high amount of certainty and less boilerplate code.

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(...)

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.