开发者

How to Test That a static Constant Value Is Passed Into MailService sendMail closure WIthout Requiring a class Prefix on the Constant?

My grails Unit Test mocks out the MailService using an Expando and redefining the sendMail method. The original code uses a static constant called EMAIL_SUBJECT without a prefix. However, when I run the test and remove from Something.EMAIL_SUBJECT the "Something." it fails the assertion saying that mockMailService.subject was passed a null How can I improve the test so the "Something." is not needed on EMAIL_SUBJECT?

Snippet of Test Code:

def createMockMailService() {

    def mockMailService = new Expando()

    mockMailService.sendMail = { callable ->

        callable.delegate = mockMailService
        callable.resolveStrategy = Closure.DELEGATE_FIRST
        callable.call()
    }

    mockMailService.subject = { header -> }

    mockMailService
}

void testThis() {
    def mockMailService = createMockMailService()

    mockMailService.subject = { assert it == "value of constant"  }

    Something something = new Something()
    something.mailService = mockMailService

    something.doSomethingThatCallsMailService()
}

Code Under Test:

class Something {

    static def EMAIL_SUBJECT = "value of constant"

    def mailService = new SomeMailThing()

    def doSomethingThatCallsMailService()开发者_StackOverflow社区 {
            mailService.sendMail {
            subject Something.EMAIL_SUBJECT // Remove Something prefix
        }
    }

}


That behaviour is strange on the first glance only. Root cause is the DELEGATE_FIRST resolution strategy combined with delegate being an Expando instance in this case. DELEGATE_FIRST looks for the EMAIL_SUBJECT property in the delegate first, which is mockMailService, which is an Expando. Expando doesn't throw groovy.lang.MissingPropertyException on missing properties, instead it returns null. Thus the property is found on the delegate and not evaluated on the owner (which would be the Something you want the property from).

You could change the resolution strategy to OWNER_FIRST. If you want to stick with DELEGATE_FIRST because the original mailService calls the sendMail closure parameter with this strategy, you can't use Expando for your mocked mail service. Instead you could use an ordinary object and do the metaprogramming on the metaClass.

It would look something like this:

def createMockMailService() {
   def mockMailService = new Object()
   mockMailService.metaClass.sendMail = { callable ->
      callable.delegate = mockMailService
      callable.resolveStrategy = Closure.DELEGATE_FIRST
      callable.call()
   }
   mockMailService.metaClass.subject = { header -> }
   mockMailService
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜