Ok. We’ve had a look at Taglibs and Controllers, it’s time to take a looks at Services. Let’s start with a simple one… My NotificationService gets called when people post new comments. But before we send a notification, we check to see whether notifications are turned on for the blog in question. This requires a little navigation of the object graph…
boolean isEmailNotifyActive(Comment comment) { BlogProperty bp = comment.blogEntry.blog.blogProperties?.find { prop -> prop.name == "emailNotify" } return bp ? Boolean.valueOf(bp.value) : false }
So the only mockery required is making sure that object graph navigation works. I have to pass in a real comment since the arg is strongly typed… and I have to return a BlogProperty
from the final find… but other than that, you can mock till it’s 1999…
void testIsEmailActive() { def bp = [name: 'emailNotify', value: 'true'] Comment.metaClass.getBlogEntry = { -> [ blog: [ blogProperties: [ bp ] ] ] } def comment = new Comment() assertTrue ns.isEmailNotifyActive(comment) bp.value = 'false' assertFalse ns.isEmailNotifyActive(comment) }
So we can mock our way through all the object graph navigation using nested maps, but just make sure that we return a BlogProperty
object at the end. We’ll go for the gold and check for both the positive and affirmative. Test coverage markers now work in IntelliJ Groovy Test cases (since 7.0.3), so let’s do a “Run with Coverage”…
Ok. Not quite 100%, but we’re underway… What’s the IDE source telling us…
Well those markers are pretty hard to see… but nothing’s red. Not sure why some things are off-pink :-)… that code certainly seems to be being covered.. Need to circle back later and check that out.
Let’s try something a little more meaty…sendMailNotification()
invokes the actual MailService
which is injected into NotificationService
by Grails. It should be trickier to mock…
def sendEmailNotification(Comment comment, String address, String baseUri) { log.debug "Sending new notification to ${address} on comment to ${comment.blogEntry.title}" mailService.send(address, """ // lots of markup stuff here around the comment """, "[Gravl] New comment for ${comment.blogEntry.title} by ${comment.author}") }
Just need to pass in a mock MailService to the NotificationService and we’re done right? Right. But MailService is defined in the NotificationService as:
MailService mailService
So we're talking concrete class - no hashmap will mock this bad boy. Given this is MockFor(March)... it's probably time to take advantage of the MockFor() support in Groovy:
void testSendEmailNotification() { String.metaClass.encodeAsWiki = { -> delegate } def bp = [name: 'emailNotify', value: 'true'] Comment.metaClass.getBlogEntry = { -> [ blog: [ blogProperties: [ bp ] ], toPermalink: {}, title: 'Sample Post' ] } def comment = new Comment(body: "sample body", author: "Joe User") def mockMail = new MockFor(MailService.class) def result = [:] mockMail.demand.send(1) { address, body, title -> result.address = address result.body = body result.title = title } mockMail.use { ns.mailService = new MailService() ns.sendEmailNotification(comment,"abc@abc.com", "http://localhost/demo/") } assertEquals "abc@abc.com", result.address assertEquals "[Gravl] New comment for Sample Post by Joe User", result.title }
The interesting bit here is how we mock out the mail service using MockFor(). My mock mail service just captures the incoming params to the send()
call (since it’s not the class under test here - I’m just check that my NotificationService is passing it the right stuff). And we’re in business.
Alrighty that’s enough mocking for one day.. Time to go and look at mocking some seemingly unmockable parts of Gravl. I’ll let you know how I go..