かんがるーさんの日記

最近自分が興味をもったものを調べた時の手順等を書いています。今は Spring Boot をいじっています。

Spring Boot でメール送信する Web アプリケーションを作る ( その14 )( Thymeleaf を利用して HTML メールを送信する )

概要

Spring Boot でメール送信する Web アプリケーションを作る ( その13 )( メール送信画面の作成7 ) の続きです。

  • 今回の手順で確認できるのは以下の内容です。
    • Thymeleaf による HTML メール送信機能を実装します。
  • 記述が長すぎてブログの途中で表示が切れてしまったため、2回に分けます。
  • 以下の仕様で実装します。
    • メール送信画面の「送信」ボタンの右側に「HTMLメール送信」ボタンを追加し、「HTMLメール送信」ボタンがクリックされたらHTMLメールを送信します。
    • 「HTMLメール送信」ボタンがクリックされた時の URL は /mailsend/sendhtml にします。
    • HTML なのでテンプレートエンジンは Velocity ではなく Thymeleaf を使用します。
    • HTMLメールのテンプレートは Ink のものを使用します。
  • 今回はテストを作成しながら実装します。
  • まさか HTML メールまでレスポンシブ対応になっているとは驚きでした。。。

ソフトウェア一覧

参考にしたサイト

  1. HTMLメール制作のコツや便利なサービスいろいろ http://www.webcreatorbox.com/webinfo/html-email/

  2. HTMLメールのレスポンシブ化
    http://nxpg.net/blog/?p=1119

  3. Ink - スマホ/タブレット対応のレスポンシブHTMLメールテンプレート
    http://www.moongift.jp/2013/11/ink-%E3%82%B9%E3%83%9E%E3%83%9B%E3%82%BF%E3%83%96%E3%83%AC%E3%83%83%E3%83%88%E5%AF%BE%E5%BF%9C%E3%81%AE%E3%83%AC%E3%82%B9%E3%83%9D%E3%83%B3%E3%82%B7%E3%83%96html%E3%83%A1%E3%83%BC%E3%83%AB%E3%83%86/

  4. Ink: A Responsive Email Framework from ZURB
    http://zurb.com/ink/

    • HTML メールのテンプレートです。レスポンシブ対応しています。
  5. Rich HTML email in Spring with Thymeleaf
    http://www.thymeleaf.org/doc/articles/springmail.html

  6. MailSenderAutoConfiguration.java
    https://github.com/spring-projects/spring-boot/blob/master/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mail/MailSenderAutoConfiguration.java

    • spring-boot-starter-mail を入れた時の AutoConfiguration 用クラスです。
  7. org.springframework.mail.javamail - Class MimeMessageHelper
    http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/mail/javamail/MimeMessageHelper.html

    • MimeMessageHelper クラスの JavaDoc です。
  8. Spring Boot Internationalization with Default Locale for Message Strings
    http://codedevstuff.blogspot.jp/2015/05/spring-boot-internationalization-with.html

    • Spring Boot で現在の Locale を取得する方法を参考にしました。

手順

ブランチの作成

  1. IntelliJ IDEA で 1.0.x-send-htmlmail ブランチを作成します。

HTML メールのテンプレートファイルの作成

  1. Ink のページの「Download Ink」ボタンをクリックし、ink-1.0.5.zip をダウンロードします。

  2. ink-1.0.5.zip を解凍し、boilerplate.html を src/main/resources/templates/mail/MAIL001 の下にコピーし、ファイル名を MAIL001-HTML-body.html に変更します。

  3. src/main/resources/templates/mail/MAIL001 の下の MAIL001-HTML-body.html を リンク先の内容 に変更します。以下のレイアウトです。

    f:id:ksby:20150524221119p:plain

    f:id:ksby:20150524221208p:plain

MAIL001MailHelper クラスの変更

  1. Thymeleaf の Rich HTML email in Spring with Thymeleafチュートリアルを見ると、HTML メールを送信するためのポイントは以下の通りです。

    • SimpleMailMessage クラスではなく MimeMessage クラスを使用します。
    • MimeMessage クラスのインスタンスを生成するためには JavaMailSender クラスの createMimeMessage メソッドを呼び出します。
    • メールを生成する時には MimeMessage クラスのメソッドを直接呼び出すのではなく、Spring Framework の MimeMessageHelper クラスを利用します。
    • メール本文をセットするために MimeMessageHelper クラスの setText メソッドを呼び出す時に、第2引数に true を指定します。第2引数は HTML メールか否かを示すフラグです。

    JavaMailSender クラスはこれまでの実装で出てきていませんでした。どうすれば利用できるようになるのか調査します。

    • 以前メール送信のために EmailService クラスに記述したのは private MailSender mailSender; であり、JavaMailSender ではありませんでした。
    • spring-boot-starter-mail 用の AutoConfiguration 用クラスを Spring Boot の GitHub で確認すると MailSenderAutoConfiguration.java でした。
    • MailSenderAutoConfiguration.java の mailSender Bean の戻り値を見ると JavaMailSenderImpl と書かれていました。Bean は JavaMailSenderImpl クラスのインスタンスを生成していましたので、@Autowired する側で MailSender ではなく JavaMailSender と定義すれば問題なく利用できるようです。
  2. src/main/java/ksbysample/webapp/email/helper/mail の下の MAIL001MailHelper.javaリンク先の内容 に変更します。

    MAIL001MailHelper クラスの generateTextUsingThymeleaf メソッドで Thymeleaf で HTML メールの本文を生成していますが、ポイントは以下の通りです。

    • テンプレートエンジンに渡す値は org.thymeleaf.context.Context クラスのインスタンスを生成して setVariable メソッドでセットします。インスタンス生成時に Locale が必要ですが、LocaleContextHolder.getLocale() で取得します。
    • Thymeleaf をテンプレートエンジンとして使用するために SpringTemplateEngine Bean を @Autowired アノテーションでインジェクションします。SpringTemplateEngine Bean は Spring Boot により自動で生成されますので、別途定義は不要です。
    • SpringTemplateEngine クラスの process メソッドを呼び出して Thymeleaf のテンプレートファイルから HTML メールの本文を生成します。
  3. テストを作成します。src/test/java/ksbysample/webapp/email/helper/mail の下の MAIL001MailHelperTest.javaリンク先の内容 に変更します。

  4. テストを実行します。MAIL001MailHelperTest クラスのクラス名にカーソルを移動した後、コンテキストメニューを表示して「Run 'MAIL001MailHelperTes...' with Coverage」メニューを選択します。

    テストが全て成功することを確認します。

    f:id:ksby:20150526034941p:plain

EmailService クラスの変更

  1. src/main/java/ksbysample/webapp/email/service の下の EmailService.javaリンク先の内容 に変更します。

  2. テストを作成します。src/test/java/ksbysample/webapp/email/service の下の EmailServiceTest.javaリンク先の内容 に変更します。

  3. テストを実行します。EmailServiceTest クラス内の「MailServerResourceを利用してメール送信する場合」クラスの testSendMail メソッドにカーソルを移動した後、コンテキストメニューを表示して「Run 'testSendMail()' with Coverage」メニューを選択します。

    テストが全て成功することを確認します。

    f:id:ksby:20150526044344p:plain

MailsendService クラスの変更

  1. src/main/java/ksbysample/webapp/email/web/mailsend の下の MailsendService.javaリンク先の内容 に変更します。

  2. テストを作成します。src/test/java/ksbysample/webapp/web/mailsend の下の MailsendServiceTest.javaリンク先の内容 に変更します。

  3. テストを実行します。MailsendServiceTest クラスのクラス名にカーソルを移動した後、コンテキストメニューを表示して「Run 'MailsendServiceTest' with Coverage」メニューを選択します。

    テストが全て成功することを確認します。

    f:id:ksby:20150527003926p:plain

次回は。。。

この続きです。commit 等も次回実施します。

ソースコード

MAIL001-HTML-body.html

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <meta name="viewport" content="width=device-width"/>
    <style type="text/css">
        /**********************************************
        * Ink v1.0.5 - Copyright 2013 ZURB Inc        *
        **********************************************/

        #outlook a {
        padding:0;
        }

        body{
        width:100% !important;
        min-width: 100%;
        -webkit-text-size-adjust:100%;
        -ms-text-size-adjust:100%;
        margin:0;
        padding:0;
        }

        .ExternalClass {
        width:100%;
        }

        .ExternalClass,
        .ExternalClass p,
        .ExternalClass span,
        .ExternalClass font,
        .ExternalClass td,
        .ExternalClass div {
        line-height: 100%;
        }

        #backgroundTable {
        margin:0;
        padding:0;
        width:100% !important;
        line-height: 100% !important;
        }

        img {
        outline:none;
        text-decoration:none;
        -ms-interpolation-mode: bicubic;
        width: auto;
        max-width: 100%;
        float: left;
        clear: both;
        display: block;
        }

        center {
        width: 100%;
        min-width: 580px;
        }

        a img {
        border: none;
        }

        p {
        margin: 0 0 0 10px;
        }

        table {
        border-spacing: 0;
        border-collapse: collapse;
        }

        td {
        word-break: break-word;
        -webkit-hyphens: auto;
        -moz-hyphens: auto;
        hyphens: auto;
        border-collapse: collapse !important;
        }

        table, tr, td {
        padding: 0;
        vertical-align: top;
        text-align: left;
        }

        hr {
        color: #d9d9d9;
        background-color: #d9d9d9;
        height: 1px;
        border: none;
        }

        /* Responsive Grid */

        table.body {
        height: 100%;
        width: 100%;
        }

        table.container {
        width: 580px;
        margin: 0 auto;
        text-align: inherit;
        }

        table.row {
        padding: 0px;
        width: 100%;
        position: relative;
        }

        table.container table.row {
        display: block;
        }

        td.wrapper {
        padding: 10px 20px 0px 0px;
        position: relative;
        }

        table.columns,
        table.column {
        margin: 0 auto;
        }

        table.columns td,
        table.column td {
        padding: 0px 0px 10px;
        }

        table.columns td.sub-columns,
        table.column td.sub-columns,
        table.columns td.sub-column,
        table.column td.sub-column {
        padding-right: 10px;
        }

        td.sub-column, td.sub-columns {
        min-width: 0px;
        }

        table.row td.last,
        table.container td.last {
        padding-right: 0px;
        }

        table.one { width: 30px; }
        table.two { width: 80px; }
        table.three { width: 130px; }
        table.four { width: 180px; }
        table.five { width: 230px; }
        table.six { width: 280px; }
        table.seven { width: 330px; }
        table.eight { width: 380px; }
        table.nine { width: 430px; }
        table.ten { width: 480px; }
        table.eleven { width: 530px; }
        table.twelve { width: 580px; }

        table.one center { min-width: 30px; }
        table.two center { min-width: 80px; }
        table.three center { min-width: 130px; }
        table.four center { min-width: 180px; }
        table.five center { min-width: 230px; }
        table.six center { min-width: 280px; }
        table.seven center { min-width: 330px; }
        table.eight center { min-width: 380px; }
        table.nine center { min-width: 430px; }
        table.ten center { min-width: 480px; }
        table.eleven center { min-width: 530px; }
        table.twelve center { min-width: 580px; }

        table.one .panel center { min-width: 10px; }
        table.two .panel center { min-width: 60px; }
        table.three .panel center { min-width: 110px; }
        table.four .panel center { min-width: 160px; }
        table.five .panel center { min-width: 210px; }
        table.six .panel center { min-width: 260px; }
        table.seven .panel center { min-width: 310px; }
        table.eight .panel center { min-width: 360px; }
        table.nine .panel center { min-width: 410px; }
        table.ten .panel center { min-width: 460px; }
        table.eleven .panel center { min-width: 510px; }
        table.twelve .panel center { min-width: 560px; }

        .body .columns td.one,
        .body .column td.one { width: 8.333333%; }
        .body .columns td.two,
        .body .column td.two { width: 16.666666%; }
        .body .columns td.three,
        .body .column td.three { width: 25%; }
        .body .columns td.four,
        .body .column td.four { width: 33.333333%; }
        .body .columns td.five,
        .body .column td.five { width: 41.666666%; }
        .body .columns td.six,
        .body .column td.six { width: 50%; }
        .body .columns td.seven,
        .body .column td.seven { width: 58.333333%; }
        .body .columns td.eight,
        .body .column td.eight { width: 66.666666%; }
        .body .columns td.nine,
        .body .column td.nine { width: 75%; }
        .body .columns td.ten,
        .body .column td.ten { width: 83.333333%; }
        .body .columns td.eleven,
        .body .column td.eleven { width: 91.666666%; }
        .body .columns td.twelve,
        .body .column td.twelve { width: 100%; }

        td.offset-by-one { padding-left: 50px; }
        td.offset-by-two { padding-left: 100px; }
        td.offset-by-three { padding-left: 150px; }
        td.offset-by-four { padding-left: 200px; }
        td.offset-by-five { padding-left: 250px; }
        td.offset-by-six { padding-left: 300px; }
        td.offset-by-seven { padding-left: 350px; }
        td.offset-by-eight { padding-left: 400px; }
        td.offset-by-nine { padding-left: 450px; }
        td.offset-by-ten { padding-left: 500px; }
        td.offset-by-eleven { padding-left: 550px; }

        td.expander {
        visibility: hidden;
        width: 0px;
        padding: 0 !important;
        }

        table.columns .text-pad,
        table.column .text-pad {
        padding-left: 10px;
        padding-right: 10px;
        }

        table.columns .left-text-pad,
        table.columns .text-pad-left,
        table.column .left-text-pad,
        table.column .text-pad-left {
        padding-left: 10px;
        }

        table.columns .right-text-pad,
        table.columns .text-pad-right,
        table.column .right-text-pad,
        table.column .text-pad-right {
        padding-right: 10px;
        }

        /* Block Grid */

        .block-grid {
        width: 100%;
        max-width: 580px;
        }

        .block-grid td {
        display: inline-block;
        padding:10px;
        }

        .two-up td {
        width:270px;
        }

        .three-up td {
        width:173px;
        }

        .four-up td {
        width:125px;
        }

        .five-up td {
        width:96px;
        }

        .six-up td {
        width:76px;
        }

        .seven-up td {
        width:62px;
        }

        .eight-up td {
        width:52px;
        }

        table.center, td.center {
        text-align: center;
        }

        h1.center,
        h2.center,
        h3.center,
        h4.center,
        h5.center,
        h6.center {
        text-align: center;
        }

        span.center {
        display: block;
        width: 100%;
        text-align: center;
        }

        img.center {
        margin: 0 auto;
        float: none;
        }

        .show-for-small,
        .hide-for-desktop {
        display: none;
        }

        /* Typography */

        body, table.body, h1, h2, h3, h4, h5, h6, p, td {
        color: #222222;
        font-family: Verdana, Roboto, 'Droid Sans', 'メイリオ', Meiryo, 'MS Pゴシック', 'ヒラギノ角ゴ Pro W3', 'Hiragino Kaku Gothic;
        font-weight: normal;
        padding:0;
        margin: 0;
        text-align: left;
        line-height: 1.3;
        }

        h1, h2, h3, h4, h5, h6 {
        word-break: normal;
        }

        h1 {font-size: 40px;}
        h2 {font-size: 36px;}
        h3 {font-size: 32px;}
        h4 {font-size: 28px;}
        h5 {font-size: 24px;}
        h6 {font-size: 20px;}
        body, table.body, p, td {font-size: 14px;line-height:19px;}

        p.lead, p.lede, p.leed {
        font-size: 18px;
        line-height:21px;
        }

        p {
        margin-bottom: 10px;
        }

        small {
        font-size: 10px;
        }

        a {
        color: #2ba6cb;
        text-decoration: none;
        }

        a:hover {
        color: #2795b6 !important;
        }

        a:active {
        color: #2795b6 !important;
        }

        a:visited {
        color: #2ba6cb !important;
        }

        h1 a,
        h2 a,
        h3 a,
        h4 a,
        h5 a,
        h6 a {
        color: #2ba6cb;
        }

        h1 a:active,
        h2 a:active,
        h3 a:active,
        h4 a:active,
        h5 a:active,
        h6 a:active {
        color: #2ba6cb !important;
        }

        h1 a:visited,
        h2 a:visited,
        h3 a:visited,
        h4 a:visited,
        h5 a:visited,
        h6 a:visited {
        color: #2ba6cb !important;
        }

        /* Panels */

        .panel {
        background: #f2f2f2;
        border: 1px solid #d9d9d9;
        padding: 10px !important;
        }

        .sub-grid table {
        width: 100%;
        }

        .sub-grid td.sub-columns {
        padding-bottom: 0;
        }

        /* Buttons */

        table.button,
        table.tiny-button,
        table.small-button,
        table.medium-button,
        table.large-button {
        width: 100%;
        overflow: hidden;
        }

        table.button td,
        table.tiny-button td,
        table.small-button td,
        table.medium-button td,
        table.large-button td {
        display: block;
        width: auto !important;
        text-align: center;
        background: #2ba6cb;
        border: 1px solid #2284a1;
        color: #ffffff;
        padding: 8px 0;
        }

        table.tiny-button td {
        padding: 5px 0 4px;
        }

        table.small-button td {
        padding: 8px 0 7px;
        }

        table.medium-button td {
        padding: 12px 0 10px;
        }

        table.large-button td {
        padding: 21px 0 18px;
        }

        table.button td a,
        table.tiny-button td a,
        table.small-button td a,
        table.medium-button td a,
        table.large-button td a {
        font-weight: bold;
        text-decoration: none;
        font-family: Helvetica, Arial, sans-serif;
        color: #ffffff;
        font-size: 16px;
        }

        table.tiny-button td a {
        font-size: 12px;
        font-weight: normal;
        }

        table.small-button td a {
        font-size: 16px;
        }

        table.medium-button td a {
        font-size: 20px;
        }

        table.large-button td a {
        font-size: 24px;
        }

        table.button:hover td,
        table.button:visited td,
        table.button:active td {
        background: #2795b6 !important;
        }

        table.button:hover td a,
        table.button:visited td a,
        table.button:active td a {
        color: #fff !important;
        }

        table.button:hover td,
        table.tiny-button:hover td,
        table.small-button:hover td,
        table.medium-button:hover td,
        table.large-button:hover td {
        background: #2795b6 !important;
        }

        table.button:hover td a,
        table.button:active td a,
        table.button td a:visited,
        table.tiny-button:hover td a,
        table.tiny-button:active td a,
        table.tiny-button td a:visited,
        table.small-button:hover td a,
        table.small-button:active td a,
        table.small-button td a:visited,
        table.medium-button:hover td a,
        table.medium-button:active td a,
        table.medium-button td a:visited,
        table.large-button:hover td a,
        table.large-button:active td a,
        table.large-button td a:visited {
        color: #ffffff !important;
        }

        table.secondary td {
        background: #e9e9e9;
        border-color: #d0d0d0;
        color: #555;
        }

        table.secondary td a {
        color: #555;
        }

        table.secondary:hover td {
        background: #d0d0d0 !important;
        color: #555;
        }

        table.secondary:hover td a,
        table.secondary td a:visited,
        table.secondary:active td a {
        color: #555 !important;
        }

        table.success td {
        background: #5da423;
        border-color: #457a1a;
        }

        table.success:hover td {
        background: #457a1a !important;
        }

        table.alert td {
        background: #c60f13;
        border-color: #970b0e;
        }

        table.alert:hover td {
        background: #970b0e !important;
        }

        table.radius td {
        -webkit-border-radius: 3px;
        -moz-border-radius: 3px;
        border-radius: 3px;
        }

        table.round td {
        -webkit-border-radius: 500px;
        -moz-border-radius: 500px;
        border-radius: 500px;
        }

        /* Outlook First */

        body.outlook p {
        display: inline !important;
        }

        /*  Media Queries */

        @media only screen and (max-width: 600px) {

        table[class="body"] img {
        width: auto !important;
        height: auto !important;
        }

        table[class="body"] center {
        min-width: 0 !important;
        }

        table[class="body"] .container {
        width: 95% !important;
        }

        table[class="body"] .row {
        width: 100% !important;
        display: block !important;
        }

        table[class="body"] .wrapper {
        display: block !important;
        padding-right: 0 !important;
        }

        table[class="body"] .columns,
        table[class="body"] .column {
        table-layout: fixed !important;
        float: none !important;
        width: 100% !important;
        padding-right: 0px !important;
        padding-left: 0px !important;
        display: block !important;
        }

        table[class="body"] .wrapper.first .columns,
        table[class="body"] .wrapper.first .column {
        display: table !important;
        }

        table[class="body"] table.columns td,
        table[class="body"] table.column td {
        width: 100% !important;
        }

        table[class="body"] .columns td.one,
        table[class="body"] .column td.one { width: 8.333333% !important; }
        table[class="body"] .columns td.two,
        table[class="body"] .column td.two { width: 16.666666% !important; }
        table[class="body"] .columns td.three,
        table[class="body"] .column td.three { width: 25% !important; }
        table[class="body"] .columns td.four,
        table[class="body"] .column td.four { width: 33.333333% !important; }
        table[class="body"] .columns td.five,
        table[class="body"] .column td.five { width: 41.666666% !important; }
        table[class="body"] .columns td.six,
        table[class="body"] .column td.six { width: 50% !important; }
        table[class="body"] .columns td.seven,
        table[class="body"] .column td.seven { width: 58.333333% !important; }
        table[class="body"] .columns td.eight,
        table[class="body"] .column td.eight { width: 66.666666% !important; }
        table[class="body"] .columns td.nine,
        table[class="body"] .column td.nine { width: 75% !important; }
        table[class="body"] .columns td.ten,
        table[class="body"] .column td.ten { width: 83.333333% !important; }
        table[class="body"] .columns td.eleven,
        table[class="body"] .column td.eleven { width: 91.666666% !important; }
        table[class="body"] .columns td.twelve,
        table[class="body"] .column td.twelve { width: 100% !important; }

        table[class="body"] td.offset-by-one,
        table[class="body"] td.offset-by-two,
        table[class="body"] td.offset-by-three,
        table[class="body"] td.offset-by-four,
        table[class="body"] td.offset-by-five,
        table[class="body"] td.offset-by-six,
        table[class="body"] td.offset-by-seven,
        table[class="body"] td.offset-by-eight,
        table[class="body"] td.offset-by-nine,
        table[class="body"] td.offset-by-ten,
        table[class="body"] td.offset-by-eleven {
        padding-left: 0 !important;
        }

        table[class="body"] table.columns td.expander {
        width: 1px !important;
        }

        table[class="body"] .right-text-pad,
        table[class="body"] .text-pad-right {
        padding-left: 10px !important;
        }

        table[class="body"] .left-text-pad,
        table[class="body"] .text-pad-left {
        padding-right: 10px !important;
        }

        table[class="body"] .hide-for-small,
        table[class="body"] .show-for-desktop {
        display: none !important;
        }

        table[class="body"] .show-for-small,
        table[class="body"] .hide-for-desktop {
        display: inherit !important;
        }
    </style>
    <style type="text/css">
        .item-label {
        color: #0000FF;
        font-weight: bold;
        }
    </style>
</head>
<body>
<table class="body">
    <tr>
        <td class="center" align="center" valign="top">
            <center>

                <table class="container">
                    <tr>
                        <td>

                            <table class="row">
                                <tr>
                                    <td class="wrapper last">

                                        <table class="twelve columns">
                                            <tr>
                                                <td>
                                                    <h1>メール送信画面からのHTMLメール</h1>

                                                    <p class="lead">画面から以下のデータが入力されました。</p>
                                                </td>
                                                <td class="expander"></td>
                                            </tr>
                                        </table>

                                    </td>
                                </tr>
                            </table>

                            <table class="row">
                                <tr>
                                    <td class="wrapper">
                                        <table class="two columns">
                                            <tr>
                                                <td class="item-label">氏名</td>
                                                <td class="expander"></td>
                                            </tr>
                                        </table>
                                    </td>
                                    <td class="wrapper last">
                                        <table class="ten columns">
                                            <tr>
                                                <td id="name" th:text="${name}">田中 太郎</td>
                                                <td class="expander"></td>
                                            </tr>
                                        </table>
                                    </td>
                                </tr>
                            </table>

                            <table class="row">
                                <tr>
                                    <td class="wrapper">
                                        <table class="two columns">
                                            <tr>
                                                <td class="item-label">性別</td>
                                                <td class="expander"></td>
                                            </tr>
                                        </table>
                                    </td>
                                    <td class="wrapper last">
                                        <table class="ten columns">
                                            <tr>
                                                <td id="sex" th:text="${sex}">男性</td>
                                                <td class="expander"></td>
                                            </tr>
                                        </table>
                                    </td>
                                </tr>
                            </table>

                            <table class="row">
                                <tr>
                                    <td class="wrapper">
                                        <table class="two columns">
                                            <tr>
                                                <td class="item-label">項目</td>
                                                <td class="expander"></td>
                                            </tr>
                                        </table>
                                    </td>
                                    <td class="wrapper last">
                                        <table class="ten columns">
                                            <tr>
                                                <td id="type" th:text="${type}">資料請求</td>
                                                <td class="expander"></td>
                                            </tr>
                                        </table>
                                    </td>
                                </tr>
                            </table>

                            <table class="row">
                                <tr>
                                    <td class="wrapper">
                                        <table class="two columns">
                                            <tr>
                                                <td class="item-label">商品</td>
                                                <td class="expander"></td>
                                            </tr>
                                        </table>
                                    </td>
                                    <td class="wrapper last">
                                        <table class="ten columns">
                                            <tr>
                                                <td id="item" th:text="${item}">商品1, 商品2, 商品3</td>
                                                <td class="expander"></td>
                                            </tr>
                                        </table>
                                    </td>
                                </tr>
                            </table>

                            <hr/>
                            <table class="row">
                                <tr>
                                    <td class="wrapper last">
                                        <table class="twelve columns">
                                            <tr>
                                                <td class="item-label">内容</td>
                                                <td class="expander"></td>
                                            </tr>
                                        </table>
                                    </td>
                                </tr>
                            </table>
                            <table class="row">
                                <tr>
                                    <td class="wrapper last">
                                        <table class="twelve columns">
                                            <tr>
                                                <td id="naiyo" th:text="${naiyo}">これはテストです。</td>
                                                <td class="expander"></td>
                                            </tr>
                                        </table>
                                    </td>
                                </tr>
                            </table>

                            <!-- container end below -->
                        </td>
                    </tr>
                </table>

            </center>
        </td>
    </tr>
</table>
</body>
</html>
  • 先頭の DOCTYPE は <!DOCTYPE html> に変更します。org.w3c.dom.Document クラスでメール本文のチェックをする際に時間がかからないようにするためです。詳細は Spring Boot + Thymeleaf の Web アプリを MockMvc でテストした時に遅かった理由とは? 参照。
  • html タグに xmlns:th="http://www.thymeleaf.org" を追加します。
  • <link rel="stylesheet" href="ink.css"> <!-- For testing only --> は削除します。
  • 最初の <style type="text/css"> /* Ink styles go here in production */ </style> の中に ink.css の中身をコピー&ペーストします。ただし /* Client-specific Styles & Reset *//* Alignment & Visibility Classes */org.xml.sax.SAXParseException: エンティティ参照では、エンティティ名は'&'の直後に指定する必要があります。 のエラーが出るため削除します。
  • Form クラスのデータを出力するところには id="name" のように id 属性を追加します。テストで値が出力されているかチェックする時に XPath で指定しやすくするためです。

MAIL001MailHelper.java

package ksbysample.webapp.email.helper.mail;

import ksbysample.webapp.email.config.Constant;
import ksbysample.webapp.email.util.VelocityUtils;
import ksbysample.webapp.email.web.mailsend.MailsendForm;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Component;
import org.thymeleaf.context.Context;
import org.thymeleaf.spring4.SpringTemplateEngine;

import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;

@Component
public class MAIL001MailHelper {

    private final String TEMPLATE_LOCATION_TEXTMAIL = "mail/MAIL001/MAIL001-body.vm";
    // templateEngine.processに渡すThymeleafのテンプレートファイルは拡張子.html付けないこと
    private final String TEMPLATE_LOCATION_HTMLMAIL = "mail/MAIL001/MAIL001-HTML-body";

    @Autowired
    private VelocityUtils velocityUtils;

    @Autowired
    private JavaMailSender mailSender;
    
    @Autowired
    private SpringTemplateEngine templateEngine;
    
    public SimpleMailMessage createMessage(MailsendForm mailsendForm) {
        SimpleMailMessage mailMessage = new SimpleMailMessage();
        mailMessage.setFrom(mailsendForm.getFromAddr());
        mailMessage.setTo(mailsendForm.getToAddr());
        mailMessage.setSubject(mailsendForm.getSubject());
        mailMessage.setText(generateTextUsingVelocity(mailsendForm));
        return mailMessage;
    }

    public MimeMessage createHtmlMessage(MailsendForm mailsendForm) throws MessagingException {
        MimeMessage mimeMessage = this.mailSender.createMimeMessage();
        MimeMessageHelper message = new MimeMessageHelper(mimeMessage, false, "UTF-8");
        message.setFrom(mailsendForm.getFromAddr());
        message.setTo(mailsendForm.getToAddr());
        message.setSubject(mailsendForm.getSubject());
        message.setText(generateTextUsingThymeleaf(mailsendForm), true);
        return message.getMimeMessage();
    }

    private String generateTextUsingVelocity(MailsendForm mailsendForm) {
        Constant constant = Constant.getInstance();
        Map<String, Object> model = new HashMap<>();
        model.put("name", mailsendForm.getName());
        model.put("sex", constant.SEX_MAP.get(mailsendForm.getSex()));
        model.put("type", constant.TYPE_MAP.get(mailsendForm.getType()));

        String itemList = null;
        if (mailsendForm.getItem() != null) {
            itemList = mailsendForm.getItem().stream()
                    .map(constant.ITEM_MAP::get)
                    .collect(Collectors.joining(", "));
        }
        model.put("item", itemList);

        model.put("naiyo", mailsendForm.getNaiyo());
        return velocityUtils.merge(this.TEMPLATE_LOCATION_TEXTMAIL, model);
    }

    private String generateTextUsingThymeleaf(MailsendForm mailsendForm) {
        Constant constant = Constant.getInstance();
        Context ctx = new Context(LocaleContextHolder.getLocale());
        ctx.setVariable("name", mailsendForm.getName());
        ctx.setVariable("sex", constant.SEX_MAP.get(mailsendForm.getSex()));
        ctx.setVariable("type", constant.TYPE_MAP.get(mailsendForm.getType()));

        String itemList = null;
        if (mailsendForm.getItem() != null) {
            itemList = mailsendForm.getItem().stream()
                    .map(constant.ITEM_MAP::get)
                    .collect(Collectors.joining(", "));
        }
        ctx.setVariable("item", itemList);

        ctx.setVariable("naiyo", mailsendForm.getNaiyo());
        
        return this.templateEngine.process(this.TEMPLATE_LOCATION_HTMLMAIL, ctx);
    }
    
}
  • テンプレートファイルのロケーションの定数を templateLocationTEMPLATE_LOCATION_TEXTMAIL へ変更します。定数なのに大文字、スネークケースで宣言していなかったので変更しました。
  • private final String TEMPLATE_LOCATION_HTMLMAIL = "mail/MAIL001/MAIL001-HTML-body"; を追加します。
  • private JavaMailSender mailSender; を追加します。
  • private SpringTemplateEngine templateEngine; を追加します。
  • createHtmlMessage メソッドを追加します。
  • generateTextUsingThymeleaf メソッドを追加します。

MAIL001MailHelperTest.java

package ksbysample.webapp.email.helper.mail;

import ksbysample.webapp.email.Application;
import ksbysample.webapp.email.config.Constant;
import ksbysample.webapp.email.web.mailsend.MailsendForm;
import org.junit.Test;
import org.junit.experimental.runners.Enclosed;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import org.yaml.snakeyaml.Yaml;

import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.StringReader;
import java.util.stream.Collectors;

import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertThat;

@RunWith(Enclosed.class)
public class MAIL001MailHelperTest {

    @RunWith(SpringJUnit4ClassRunner.class)
    @SpringApplicationConfiguration(classes = Application.class)
    @WebAppConfiguration
    public static class テキストメール生成のテスト {

        private final MailsendForm mailsendFormSimple
                = (MailsendForm) new Yaml().load(getClass().getResourceAsStream("/ksbysample/webapp/email/web/mailsend/mailsendForm_simple.yml"));
        private final MailsendForm mailsendFormMinimum
                = (MailsendForm) new Yaml().load(getClass().getResourceAsStream("/ksbysample/webapp/email/web/mailsend/mailsendForm_minimum.yml"));

        @Autowired
        private MAIL001MailHelper mail001MailHelper;

        @Test
        public void MailsendFormの全てに値がセットされている場合() throws Exception {
            SimpleMailMessage message = mail001MailHelper.createMessage(mailsendFormSimple);
            assertThat(message.getFrom(), is(mailsendFormSimple.getFromAddr()));
        }

        @Test
        public void MailsendFormの必須項目のみ値がセットされている場合() throws Exception {
            SimpleMailMessage message = mail001MailHelper.createMessage(mailsendFormMinimum);
            assertThat(message.getFrom(), is(mailsendFormMinimum.getFromAddr()));
        }

    }

    @RunWith(SpringJUnit4ClassRunner.class)
    @SpringApplicationConfiguration(classes = Application.class)
    @WebAppConfiguration
    public static class HTMLメール生成のテスト {

        private final MailsendForm mailsendFormSimple
                = (MailsendForm) new Yaml().load(getClass().getResourceAsStream("/ksbysample/webapp/email/web/mailsend/mailsendForm_simple.yml"));
        private final MailsendForm mailsendFormMinimum
                = (MailsendForm) new Yaml().load(getClass().getResourceAsStream("/ksbysample/webapp/email/web/mailsend/mailsendForm_minimum.yml"));

        @Autowired
        private MAIL001MailHelper mail001MailHelper;

        @Test
        public void MailsendFormの全てに値がセットされている場合() throws Exception {
            MimeMessage message = mail001MailHelper.createHtmlMessage(mailsendFormSimple);

            // from, Subject
            InternetAddress address = (InternetAddress) message.getFrom()[0];
            assertThat(address.getAddress(), is(mailsendFormSimple.getFromAddr()));
            assertThat(message.getSubject(), is(mailsendFormSimple.getSubject()));

            // メール本文
            Document document = DocumentBuilderFactory
                    .newInstance()
                    .newDocumentBuilder()
                    .parse(new InputSource(new StringReader((String) message.getContent())));
            Constant constant = Constant.getInstance();
            assertThat(document, hasXPath("//*[@id=\"name\"]", equalTo(mailsendFormSimple.getName())));
            assertThat(document, hasXPath("//*[@id=\"sex\"]", equalTo(constant.SEX_MAP.get(mailsendFormSimple.getSex()))));
            assertThat(document, hasXPath("//*[@id=\"type\"]", equalTo(constant.TYPE_MAP.get(mailsendFormSimple.getType()))));
            assertThat(document, hasXPath("//*[@id=\"item\"]"
                    , equalTo(
                    mailsendFormSimple.getItem().stream()
                            .map(constant.ITEM_MAP::get)
                            .collect(Collectors.joining(", ")))));
            assertThat(document, hasXPath("//*[@id=\"naiyo\"]", equalTo(mailsendFormSimple.getNaiyo())));
        }

        @Test
        public void MailsendFormの必須項目のみ値がセットされている場合() throws Exception {
            MimeMessage message = mail001MailHelper.createHtmlMessage(mailsendFormMinimum);

            // from, Subject
            InternetAddress address = (InternetAddress) message.getFrom()[0];
            assertThat(address.getAddress(), is(mailsendFormMinimum.getFromAddr()));
            assertThat(message.getSubject(), is(mailsendFormMinimum.getSubject()));

            // メール本文
            Document document = DocumentBuilderFactory
                    .newInstance()
                    .newDocumentBuilder()
                    .parse(new InputSource(new StringReader((String) message.getContent())));
            Constant constant = Constant.getInstance();
            assertThat(document, hasXPath("//*[@id=\"name\"]", equalTo("")));
            assertThat(document, hasXPath("//*[@id=\"sex\"]", equalTo("")));
            assertThat(document, hasXPath("//*[@id=\"type\"]", equalTo("")));
            assertThat(document, hasXPath("//*[@id=\"item\"]", equalTo("")));
            assertThat(document, hasXPath("//*[@id=\"naiyo\"]", equalTo("")));
        }

    }

}
  • ネストしたクラスで構造化し、既存のテストを「テキストメール生成のテスト」クラスに入れて、「HTMLメール生成のテスト」クラスを追加します。
  • MAIL001MailHelperTest クラスには @RunWith(Enclosed.class) を追加します。
  • 「HTMLメール生成のテスト」クラスに「テキストメール生成のテスト」クラスと同じテストメソッドを定義し、実装します。
  • 「HTMLメール生成のテスト」クラスのテストメソッドではメール本文のチェックを追加します。org.w3c.dom.Document クラスで HTML メールの本文を解析した後、assertThat(document, hasXPath("...", equalTo("..."))); でチェックします。

EmailService.java

package ksbysample.webapp.email.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.stereotype.Service;

import javax.mail.internet.MimeMessage;

@Service
public class EmailService {

    @Autowired
    private JavaMailSender mailSender;

    public void sendSimpleMail(SimpleMailMessage mailMessage) {
        mailSender.send(mailMessage);
    }
    
    public void sendMail(MimeMessage message) {
        mailSender.send(message);
    }

}
  • private MailSender mailSender;private JavaMailSender mailSender; へ変更します。MailSender のままだと send メソッドで MimeMessage を送信できませんでした。
  • sendMail メソッドを追加します。

EmailServiceTest.java

    @RunWith(SpringJUnit4ClassRunner.class)
    @SpringApplicationConfiguration(classes = Application.class)
    @WebAppConfiguration
    public static class MailServerResourceを利用してメール送信する場合 {

        @Rule
        @Autowired
        public MailServerResource mailServer;

        @Autowired
        private JavaMailSender mailSender;
        
        @Autowired
        private EmailService emailService;

        @Test
        public void testSendSimpleMail() throws Exception {
            SimpleMailMessage message = new SimpleMailMessage();
            message.setFrom("test@sample.com");
            message.setTo("xxx@yyy.zzz");
            message.setSubject("テスト");
            message.setText("これはテストです");
            emailService.sendSimpleMail(message);

            assertThat(mailServer.getMessagesCount(), is(1));
            MimeMessage receiveMessage = mailServer.getFirstMessage();
            assertThat(receiveMessage.getFrom()[0], is(new InternetAddress("test@sample.com")));
            assertThat(receiveMessage.getAllRecipients()[0], is(new InternetAddress("xxx@yyy.zzz")));
            assertThat(receiveMessage.getSubject(), is("テスト"));
            assertThat(receiveMessage.getContent(), is("これはテストです"));
        }

        @Test
        public void testSendMail() throws Exception {
            MimeMessage mimeMessage = mailSender.createMimeMessage();
            MimeMessageHelper message = new MimeMessageHelper(mimeMessage);
            message.setFrom("test@sample.com");
            message.setTo("xxx@yyy.zzz");
            message.setSubject("テスト");
            message.setText("これはテストです");
            emailService.sendMail(message.getMimeMessage());
            
            assertThat(mailServer.getMessagesCount(), is(1));
            MimeMessage receiveMessage = mailServer.getFirstMessage();
            assertThat(receiveMessage.getFrom()[0], is(new InternetAddress("test@sample.com")));
            assertThat(receiveMessage.getAllRecipients()[0], is(new InternetAddress("xxx@yyy.zzz")));
            assertThat(receiveMessage.getSubject(), is("テスト"));
            assertThat(receiveMessage.getContent(), is("これはテストです"));
        }

    }
  • 「MailServerResourceを利用してメール送信する場合」クラスを変更します。
    • private JavaMailSender mailSender; を追加します。
    • testSendMail メソッドを追加します。

MailsendService.java

package ksbysample.webapp.email.web.mailsend;

import ksbysample.webapp.email.dao.EmailDao;
import ksbysample.webapp.email.dao.EmailItemDao;
import ksbysample.webapp.email.entity.Email;
import ksbysample.webapp.email.entity.EmailItem;
import ksbysample.webapp.email.helper.mail.MAIL001MailHelper;
import ksbysample.webapp.email.service.EmailService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.stereotype.Service;

import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;

@Service
public class MailsendService {

    @Autowired
    private EmailDao emailDao;

    @Autowired
    private EmailItemDao emailItemDao;

    @Autowired
    private MAIL001MailHelper mail001MailHelper;

    @Autowired
    private EmailService emailService;

    public void saveAndSendEmail(MailsendForm mailsendForm) {
        // 入力されたデータを email, email_item テーブルに保存する
        saveEmail(mailsendForm);

        // メールを送信する
        sendEmail(mailsendForm);
    }

    public void saveAndSendHtmlEmail(MailsendForm mailsendForm) throws MessagingException {
        // 入力されたデータを email, email_item テーブルに保存する
        saveEmail(mailsendForm);

        // HTMLメールを送信する
        sendHtmlEmail(mailsendForm);
    }

    private void saveEmail(MailsendForm mailsendForm) {
        // email テーブルに保存する
        Email email = new Email();
        BeanUtils.copyProperties(mailsendForm, email);
        emailDao.insert(email);

        // email_item テーブルに保存する
        EmailItem emailItem = new EmailItem();
        if (mailsendForm.getItem() != null) {
            for (String item : mailsendForm.getItem()) {
                emailItem.setEmailItemId(null);
                emailItem.setEmailId(email.getEmailId());
                emailItem.setItem(item);
                emailItemDao.insert(emailItem);
            }
        }
    }

    private void sendEmail(MailsendForm mailsendForm) {
        SimpleMailMessage message = mail001MailHelper.createMessage(mailsendForm);
        emailService.sendSimpleMail(message);
    }

    private void sendHtmlEmail(MailsendForm mailsendForm) throws MessagingException {
        MimeMessage message = mail001MailHelper.createHtmlMessage(mailsendForm);
        emailService.sendMail(message);
    }

}
  • saveAndSendHtmlEmail メソッドを追加します。
  • sendHtmlEmail メソッドを追加します。
  • saveEmail メソッド、sendEmail メソッドのアクセス修飾子を public → private に変更します。
  • sendEmail メソッド内の変数名を sendHtmlEmail メソッドのものに合わせるために mailMessagemessage へ変更します。

MailsendServiceTest.java

package ksbysample.webapp.email.web.mailsend;

import com.google.common.io.Files;
import ksbysample.webapp.email.Application;
import ksbysample.webapp.email.config.Constant;
import ksbysample.webapp.email.test.MailServerResource;
import ksbysample.webapp.email.test.TableDataAssert;
import ksbysample.webapp.email.test.TestDataResource;
import org.dbunit.dataset.IDataSet;
import org.dbunit.dataset.csv.CsvDataSet;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import org.yaml.snakeyaml.Yaml;

import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.sql.DataSource;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.File;
import java.io.StringReader;
import java.nio.charset.StandardCharsets;
import java.util.stream.Collectors;

import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertThat;

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class)
@WebAppConfiguration
public class MailsendServiceTest {

    // MailsendFormの全てに値がセットされているテストデータ
    private final MailsendForm mailsendFormSimple
            = (MailsendForm) new Yaml().load(getClass().getResourceAsStream("mailsendForm_simple.yml"));
    // MailsendFormの必須項目のみ値がセットされているテストデータ
    private final MailsendForm mailsendFormMinimum
            = (MailsendForm) new Yaml().load(getClass().getResourceAsStream("mailsendForm_minimum.yml"));

    @Rule
    @Autowired
    public TestDataResource testDataResource;

    @Rule
    @Autowired
    public MailServerResource mailServer;

    @Autowired
    private DataSource dataSource;

    @Autowired
    private MailsendService mailsendService;

    @Test
    public void mailsendFormSimpleでテキストメールを送信する場合() throws Exception {
        mailsendService.saveAndSendEmail(mailsendFormSimple);

        // email, email_item テーブルに保存されているか確認する
        IDataSet dataSet = new CsvDataSet(new File("src/test/resources/ksbysample/webapp/email/web/mailsend/testdata/simple"));
        TableDataAssert tableDataAssert = new TableDataAssert(dataSet, dataSource);
        tableDataAssert.assertEquals("email", new String[]{"email_id"});
        tableDataAssert.assertEquals("email_item", new String[]{"email_item_id", "email_id"});

        // メールが送信されているか確認する
        assertThat(mailServer.getMessagesCount(), is(1));
        MimeMessage receiveMessage = mailServer.getFirstMessage();
        assertThat(receiveMessage.getFrom()[0], is(new InternetAddress("test@sample.com")));
        assertThat(receiveMessage.getAllRecipients()[0], is(new InternetAddress("xxx@yyy.zzz")));
        assertThat(receiveMessage.getSubject(), is("テスト"));
        String mailsendFormSimpleMail
                = Files.toString(new File(getClass().getResource("mailsendForm_simple_mail.txt").toURI()), StandardCharsets.UTF_8);
        assertThat(receiveMessage.getContent(), is(mailsendFormSimpleMail));
    }

    @Test
    public void mailsendFormMinimumでテキストメールを送信する場合() throws Exception {
        mailsendService.saveAndSendEmail(mailsendFormMinimum);

        // email, email_item テーブルに保存されているか確認する
        IDataSet dataSet = new CsvDataSet(new File("src/test/resources/ksbysample/webapp/email/web/mailsend/testdata/minimum"));
        TableDataAssert tableDataAssert = new TableDataAssert(dataSet, dataSource);
        tableDataAssert.assertEquals("email", new String[]{"email_id"});
        tableDataAssert.assertEquals("email_item", new String[]{"email_item_id", "email_id"});

        // メールが送信されているか確認する
        assertThat(mailServer.getMessagesCount(), is(1));
        MimeMessage receiveMessage = mailServer.getFirstMessage();
        assertThat(receiveMessage.getFrom()[0], is(new InternetAddress("test@sample.com")));
        assertThat(receiveMessage.getAllRecipients()[0], is(new InternetAddress("xxx@yyy.zzz")));
        assertThat(receiveMessage.getSubject(), is("テスト"));
        String mailsendFormMinimumMail
                = Files.toString(new File(getClass().getResource("mailsendForm_minimum_mail.txt").toURI()), StandardCharsets.UTF_8);
        assertThat(receiveMessage.getContent(), is(mailsendFormMinimumMail));
    }

    @Test
    public void mailsendFormSimpleでHTMLメールを送信する場合() throws Exception {
        mailsendService.saveAndSendHtmlEmail(mailsendFormSimple);

        // email, email_item テーブルに保存されているか確認する
        IDataSet dataSet = new CsvDataSet(new File("src/test/resources/ksbysample/webapp/email/web/mailsend/testdata/simple"));
        TableDataAssert tableDataAssert = new TableDataAssert(dataSet, dataSource);
        tableDataAssert.assertEquals("email", new String[]{"email_id"});
        tableDataAssert.assertEquals("email_item", new String[]{"email_item_id", "email_id"});

        // メールが送信されているか確認する
        assertThat(mailServer.getMessagesCount(), is(1));
        MimeMessage receiveMessage = mailServer.getFirstMessage();
        assertThat(receiveMessage.getFrom()[0], is(new InternetAddress("test@sample.com")));
        assertThat(receiveMessage.getAllRecipients()[0], is(new InternetAddress("xxx@yyy.zzz")));
        assertThat(receiveMessage.getSubject(), is("テスト"));

        // メール本文
        Document document = DocumentBuilderFactory
                .newInstance()
                .newDocumentBuilder()
                .parse(new InputSource(new StringReader((String) receiveMessage.getContent())));
        Constant constant = Constant.getInstance();
        assertThat(document, hasXPath("//*[@id=\"name\"]", equalTo(mailsendFormSimple.getName())));
        assertThat(document, hasXPath("//*[@id=\"sex\"]", equalTo(constant.SEX_MAP.get(mailsendFormSimple.getSex()))));
        assertThat(document, hasXPath("//*[@id=\"type\"]", equalTo(constant.TYPE_MAP.get(mailsendFormSimple.getType()))));
        assertThat(document, hasXPath("//*[@id=\"item\"]"
                , equalTo(
                mailsendFormSimple.getItem().stream()
                        .map(constant.ITEM_MAP::get)
                        .collect(Collectors.joining(", ")))));
        assertThat(document, hasXPath("//*[@id=\"naiyo\"]", equalTo(mailsendFormSimple.getNaiyo())));
    }

    @Test
    public void mailsendFormMinimumでHTMLメールを送信する場合() throws Exception {
        mailsendService.saveAndSendHtmlEmail(mailsendFormMinimum);

        // email, email_item テーブルに保存されているか確認する
        IDataSet dataSet = new CsvDataSet(new File("src/test/resources/ksbysample/webapp/email/web/mailsend/testdata/minimum"));
        TableDataAssert tableDataAssert = new TableDataAssert(dataSet, dataSource);
        tableDataAssert.assertEquals("email", new String[]{"email_id"});
        tableDataAssert.assertEquals("email_item", new String[]{"email_item_id", "email_id"});

        // メールが送信されているか確認する
        assertThat(mailServer.getMessagesCount(), is(1));
        MimeMessage receiveMessage = mailServer.getFirstMessage();
        assertThat(receiveMessage.getFrom()[0], is(new InternetAddress("test@sample.com")));
        assertThat(receiveMessage.getAllRecipients()[0], is(new InternetAddress("xxx@yyy.zzz")));
        assertThat(receiveMessage.getSubject(), is("テスト"));

        // メール本文
        Document document = DocumentBuilderFactory
                .newInstance()
                .newDocumentBuilder()
                .parse(new InputSource(new StringReader((String) receiveMessage.getContent())));
        Constant constant = Constant.getInstance();
        assertThat(document, hasXPath("//*[@id=\"name\"]", equalTo("")));
        assertThat(document, hasXPath("//*[@id=\"sex\"]", equalTo("")));
        assertThat(document, hasXPath("//*[@id=\"type\"]", equalTo("")));
        assertThat(document, hasXPath("//*[@id=\"item\"]", equalTo("")));
        assertThat(document, hasXPath("//*[@id=\"naiyo\"]", equalTo("")));
    }

}
  • 既存のテストメソッド名を変更します。
    • MailsendFormの全てに値がセットされている場合mailsendFormSimpleでテキストメールを送信する場合
    • MailsendFormの必須項目のみ値がセットされている場合mailsendFormMinimumでテキストメールを送信する場合
  • 「mailsendFormSimpleでHTMLメールを送信する場合」メソッドを追加します。
  • 「mailsendFormMinimumでHTMLメールを送信する場合」メソッドを追加します。
  • メールサーバ経由での HTML メールの送信結果を確認するために、メール本文のチェック処理は MAIL001MailHelperTest クラスで作成したものを持ってきました。同じ処理をコピーしているのですが、テストの場合も同じ処理ならばチェック処理としてどこかにまとめた方がよいのでしょうか? それとも同じチェックをしていること自体が何か間違っているのでしょうか? ちょっと悩みつつも今回はこのままで行きます。

履歴

2015/05/27
初版発行。