tokuhirom's Blog

JavaMail をモッキングしてテストしたい。

Java でメールを送るなら JavaMail で送るのが普通らしい。

とはいえ、生の JavaMail で書いてるとダルいので適宜以下のようにラッパをかましたりする。 本文は Freemarker で処理する、的な。Fluent interface っぽくてクール。

本当はいぜん Perl で書いていたような、Template file 側に Subject 入れられるようなのを書きたいが、書く時間がない。 誰か書くのを待つか、だれかが教えてくれるのをじっと待とう。

public class OreMailer {
    private final MimeMessage message;
    private final Configuration freemarkerConfiguration;

    public OreMailer(Class<?> klass) throws IOException {
        Properties properties = System.getProperties();
        if (properties.getProperty("mail.smtp.host") == null) {
            properties.setProperty("mail.smtp.host", "localhost");
        }
        Session session = Session.getDefaultInstance(properties);

        this.message = new MimeMessage(session);
        this.freemarkerConfiguration = this.buildFreemarkerConfiguration(klass);
    }

    public MimeMessage getMessage() {
        return this.message;
    }

    public void send() throws MessagingException {
        Transport.send(this.message);
    }

    public OreMailer to(String... addresses) throws MessagingException {
        for (String address : addresses) {
            message.addRecipient(Message.RecipientType.TO, new InternetAddress(address));
        }
        return this;
    }

    public OreMailer from(String address) throws MessagingException {
        message.setFrom(address);
        return this;
    }

    public OreMailer renderBodyWithFreemarker(String tmplName, Object dataModel) throws IOException, TemplateException,
            MessagingException {
        Template template = this.freemarkerConfiguration.getTemplate(tmplName);
        final StringWriter writer = new StringWriter();
        template.process(dataModel, writer);
        final String bodyString = writer.toString();
        this.message.setText(bodyString, "UTF-8");
        return this;
    }

    /**
    * Create new Freemarker configuration.
    */
    private Configuration buildFreemarkerConfiguration(Class<?> klass) throws IOException {
        final Configuration cfg = new Configuration();
        cfg.setClassForTemplateLoading(klass, "/somailer");
        cfg.setDefaultEncoding("UTF-8");
        cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
        cfg.setIncompatibleImprovements(new Version(2, 3, 20)); // FreeMarker
        return cfg;
    }

    public OreMailer subject(String s) throws MessagingException {
        this.message.setSubject(s, "UTF-8");
        return this;
    }
}

で、テストどうするかってことなんだけど、mock-javamail ってのを使うのがいいようだ。

<dependency>
    <groupId>org.jvnet.mock-javamail</groupId>
    <artifactId>mock-javamail</artifactId>
    <version>1.9</version>
    <scope>test</scope>
</dependency>

とか pom.xml に書いておいて、以下のようにテストコードを書いていけばいいだけ。なんか依存にいれるだけでいい感じに送信メールをキャプチャしてくれるようだ。

List<Message> inbox = Mailbox.get("[email protected]");
assertEquals(inbox.size(),1); // was the e-mail really sent?

mock-javamail だいぶ長らくリリースされてないし、どうなのかな~と思ったけど、 JavaMail 自体べつに進化してないので問題ないようだ。 バックワードコンパチビリティ最高!!!

ちなみに開発環境で動作確認するときは以下のように python でモックサーバーあげればよい。

sudo python -u -m smtpd -c DebuggingServer :25

参考文献