5

grailsEメールプラグインを使用してEメールを送信しているGrailsコントローラーをテストしたいと思います。sendMailインタラクションが機能するためにクロージャーをモックする方法が正確にわかりません。これが私の最新バージョンのテストコードです。

def 'controller should send a multipart email'() {
    given: 'a mocked mailService'
        controller.mailService = Mock(grails.plugin.mail.MailService)
        controller.mailService.sendMail(*_) >> Mock(org.springframework.mail.MailMessage)
    when:
        controller.sendNow()
    then:
        1* _.multipart(true)
}

コントローラのコードは、期待するもののようになります。例:

def mailService
def sendNow() {
    mailService.sendMail {
        multipart true
        to 'example@example.org'
        from 'me@here.com'
        subject 'a subject'
        body 'a body'
    }
}

このテストを実行すると、multipartインタラクションの呼び出しが1ではなく0になります。given:ブロックの2行目は疑わしいようですが、テストのClosure代わりにモックを実行しようとするとorg.springframework.mail.MailMessageクラッシュします。また、コントローラー自体が期待どおりに機能することにも言及する必要があります(最初に単体テストを理解するのが待ちきれませんでした)。

編集済み

ああ、数時間後に新鮮な心でコードを見ると、上記のコードが機能しない理由がわかります。キャッチmultipartやその他のDSL呼び出しを行うには、sendMailメソッドではなく、クロージャー自体をモックする必要があります(クロージャーはコントローラー自体の内部で定義されているため、これはできません)。私がおそらくできることは、メソッドの引数をチェックして、sendMail必要なものがすべて渡されたことを確認することです。

4

4 に答える 4

4

私は次の方法でスポックでこれを達成することができました:

def messageBuilder
def bodyParams
def setup(){
    def mockMailService = new MockFor(MailService)
    mockMailService.ignore.sendMail{ callable ->
        messageBuilder = new MailMessageBuilder(null, new ConfigObject())
        messageBuilder.metaClass.body = { Map params ->
            bodyParams = params
        }
        callable.delegate = messageBuilder
        callable.resolveStrategy = Closure.DELEGATE_FIRST
        callable.call()
    }
    service.mailService = mockMailService.proxyInstance()
}

そしてサンプルテスト:

def "sendEmailReceipt_passesCorrectParams"(){
    when:
        def receiptItems = [] << [item: "item1", price: 100]
        service.sendEmailReceipt(receiptItems, "some@email.com")

    then:
        messageBuilder.message.to[0] == "some@email.com"
        messageBuilder.message.subject == "My subject"
        bodyParams.view == "/mailtemplates/emailReceipt"
        bodyParams.model.receiptItems == data
}
于 2013-08-27T04:24:13.183 に答える
3

greenMailプラグインをインストールして、統合テストで使用できます。

グリーンメールプラグインのホームページから:

import com.icegreen.greenmail.util.*

class GreenmailTests extends GroovyTestCase {
    def mailService
    def greenMail    

    void testSendMail() {
        Map mail = [message:'hello world', from:'from@piragua.com', to:'to@piragua.com', subject:'subject']        

        mailService.sendMail {
            to mail.to
            from mail.from
            subject mail.subject
            body mail.message
        }        

        assertEquals(1, greenMail.getReceivedMessages().length)        
        def message = greenMail.getReceivedMessages()[0]        
        assertEquals(mail.message, GreenMailUtil.getBody(message))
        assertEquals(mail.from, GreenMailUtil.getAddressList(message.from))
        assertEquals(mail.subject, message.subject)
    }    

    void tearDown() {
        greenMail.deleteAllMessages()
    }
}

私はスポックの専門家ではありませんが、このjunitテストをスポックスタイルに変換できるはずです。

ソース: http: //grails.org/plugin/greenmail

Udpate、sendMailをモックすることによる代替

これはGregorのアップデートに対する答えです。私の意見では、sendMailメソッドをモックする必要があり、このメソッドの中には、クロージャーで使用されるさまざまなプロパティとメソッドを実装するスタブがあります。それを評価者と呼びましょう。クロージャのデリゲートをevaluatroに初期化し、クロージャを実行します。評価者はアサーションを持っている必要があります。ここでは、より多くのjunitの概念を使用していることがわかります。それをスポックの概念にどれだけ簡単に変換できるかわかりません。スポックの動作確認機能をご利用いただければと思います。

class MailVerifier {
    void multiPart(boolean v){
        //...
    }

    void to(String address){
        //...
    }

    boolean isVerified() {
        //check internal state obtained by the appropriate invocation of the methods
    }
}

def sendMail(Closure mailDefintion) {
    def evaluator = createMailVerifier()
    mailDefinition.delegate = evaluator

    mailDefinition()

    assert evaluator.verified
}
于 2012-05-23T08:31:49.440 に答える
1

ここでプラグインテストを見てください:プラグイン統合テストとここで:プラグインユニットテスト。私の意見では、すべてのMailService依存関係(メールメッセージを作成するファクトリとビルダー)をモックするのは難しいでしょう。コントローラーのsendNowが呼び出された場合にのみテストを行うことになります。

編集

私はこの答えを見つけました。それによると、あなたは試すことができます:

def 'controller should send a multipart email'() {
    given: 'a mocked mailService'
        def mockMailService = new Object()
        def mockMessageBuilder = Mock(MessageBuilder)
        mockMailService.metaClass.sendMail = { callable ->
            callable.delegate = mockMessageBuilder
            callable.resolveStrategy = Closure.DELEGATE_FIRST
            callable.call()
        }
        controller.mailService = mockMailService
    when:
        controller.sendNow()
    then:
        1* mockMessageBuilder.multipart(true)

}

于 2012-05-23T09:13:09.807 に答える
1
def mailService = Mock(MailService)
mockMailService.metaClass.sendMail = { ... your logic ... }
controller.mailService = mailService
于 2012-05-24T15:57:40.200 に答える