読者です 読者をやめる 読者になる 読者になる

かんがるーさんの日記

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

Spring Boot で書籍の貸出状況確認・貸出申請する Web アプリケーションを作る ( その29 )( 貸出状況取得タスクの作成3 )

概要

Spring Boot で書籍の貸出状況確認・貸出申請する Web アプリケーションを作る ( その28 )( 貸出状況取得タスクの作成2 ) の続きです。

  • 今回の手順で確認できるのは以下の内容です。
    • 貸出状況取得タスクの作成
      • テストの作成

参照したサイト・書籍

  1. com.google.common.io - Class Files
    http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/io/Files.html

    • Mail001Helper クラスのテストを書く際に、比較対象のメール本文を記述したファイルを読み込んで String 型の変数にセットするために Files クラスの toString メソッドを利用しました。
  2. Read file line by line in java 8: Streams of Lines Example
    http://howtodoinjava.com/2014/05/04/read-file-line-by-line-in-java-8-streams-of-lines-example/

    • Guava の Files.toString メソッドではなく java.nio.File パッケージの Files.lines メソッドあたりが使えないかと思い、調査した時に参照しました。
    • Files.lines をそのまま単に Stream API で処理するのではなく、一旦 try-with-resources 構文で囲っているのが印象に残ったのでメモとして残しておきます。リソースのクローズ忘れに注意しないといけないとは知りませんでした。。。

目次

  1. テスト作成対象のクラスを決める
  2. calilapi のレスポンス展開用のクラスを reponse パッケージの下に移動する
  3. EmailService.java のテストクラスを ksbysample-webapp-email から持ってくる
  4. Mail001Helper クラスのテストの作成
  5. InquiringStatusOfBookQueueListener クラスのテストの作成
  6. LendingUserDetailsHelper クラスのテストの作成
  7. UploadBooklistFormValidator クラスのテストの作成
  8. 全てのテストが成功するか確認する
  9. commit、Push、Pull Request、マージ
  10. 次回は。。。
  11. メモ書き

手順

テスト作成対象のクラスを決める

  1. Gradle projects View から printClassWhatNotMakeTest タスクを実行します。

    f:id:ksby:20151031064528p:plain

  2. 出力されたクラスに対して以下の対応を行います。

    • src/main/java/ksbysample/webapp/lending/service/calilapi の下に作成したレスポンス展開用のクラスは reponse パッケージを作成してその下に移動し、src/main/java/ksbysample/webapp/lending/service/calilapi/response を printClassWhatNotMakeTest タスクのチェック対象外にします。

    • src/main/java/ksbysample/webapp/lending/service/EmailService.java はテストクラスも ksbysample-webapp-email から持ってきます。

    • 以下のクラスはテストを作成します。

      • src/main/java/ksbysample/webapp/lending/helper/mail/Mail001Helper.java
      • src/main/java/ksbysample/webapp/lending/listener/rabbitmq/InquiringStatusOfBookQueueListener.java
      • src/main/java/ksbysample/webapp/lending/security/LendingUserDetailsHelper.java
      • src/main/java/ksbysample/webapp/lending/web/booklist/UploadBooklistFormValidator.java

calilapi のレスポンス展開用のクラスを reponse パッケージの下に移動する

  1. src/main/java/ksbysample/webapp/lending/service/calilapi の下に response パッケージを作成します。

  2. src/main/java/ksbysample/webapp/lending/service/calilapi の下の以下のクラスを reponse パッケージの下へ移動します。

  3. src/main/java/ksbysample/webapp/lending/service/calilapi/response を printClassWhatNotMakeTest タスクのチェック対象外にします。build.gradle を リンク先の内容 に変更します。

EmailService.java のテストクラスを ksbysample-webapp-email から持ってくる

  1. https://github.com/ksby/ksbysample-webapp-email/tree/1.0.x/src/test/java/ksbysample/webapp/email/service の下にある EmailServiceTest.java を持ってきて、src/test/java/ksbysample/webapp/lending/service の下に配置します。配置後 public static class MailServerResourceを利用してメール送信する場合{ ... } 以外のネストクラスを削除し、リンク先の内容 に変更します。

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

    テストが成功することが確認できます。

    f:id:ksby:20151104003400p:plain

Mail001Helper クラスのテストの作成

  1. src/main/java/ksbysample/webapp/lending/helper/mail の下の Mail001Helper.java で「Create Test」ダイアログを表示し、テストクラスを作成します。

    f:id:ksby:20151104005037p:plain

    src/test/java/ksbysample/webapp/lending/helper/mail の下に Mail001HelperTest.java が作成されます。

  2. src/test/resources/ksbysample/webapp/lending の下に helper/mail/assertdata/001 ディレクトリを作成します。

  3. src/test/resources/ksbysample/webapp/lending/helper/mail/assertdata/001 の下に message.txt を作成します。作成後、リンク先の内容 に変更します。

  4. src/test/java/ksbysample/webapp/lending/helper/mail の下の Mail001HelperTest.javaリンク先の内容 に変更します。

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

    テストが成功することが確認できます。

    f:id:ksby:20151104020753p:plain

InquiringStatusOfBookQueueListener クラスのテストの作成

  1. src/main/java/ksbysample/webapp/lending/listener/rabbitmq の下の InquiringStatusOfBookQueueListener.java で「Create Test」ダイアログを表示し、テストクラスを作成します。

    f:id:ksby:20151104215045p:plain

    src/test/java/ksbysample/webapp/lending/listener/rabbitmq の下に InquiringStatusOfBookQueueListenerTest.java が作成されます。

  2. DB にセットするテストデータを定義します。src/test/resources/ksbysample/webapp/lending の下に listener/rabbitmq/testdata/001 ディレクトリを作成します。

  3. src/test/resources/ksbysample/webapp/lending/helper/mail/testdata/001 の下に table-ordering.txt, lending_app.csv, lending_book.csv を作成します。作成後、リンク先の内容 に変更します。

  4. 検証用データを定義します。src/test/resources/ksbysample/webapp/lending/listener/rabbitmq の下に assertdata/001 ディレクトリを作成します。

  5. src/test/resources/ksbysample/webapp/lending/listener/rabbitmq/assertdata/001 の下に table-ordering.txt, lending_app.csv, lending_book.csv を作成します。作成後、リンク先の内容 に変更します。

  6. テストでは receiveMessage メソッド内の calilApiService.check をモックに入れ替えてカーリルの蔵書検索 WebAPI を呼び出さないようにします。モックで Book クラス及び関連クラス ( SystemData クラス、Libkey クラス ) のインスタンスを生成しやすくするために、この3つのクラスに lombok の @AllArgsConstructor アノテーションを付加します ( @NoArgsConstructor もないと実行時にエラーが出るため一緒に付加します )。src/main/java/ksbysample/webapp/lending/listener/rabbitmq/response の下の Book.java, SystemData.java, Libkey.javaリンク先の内容 に変更します。

  7. src/test/java/ksbysample/webapp/lending/listener/rabbitmq の下の InquiringStatusOfBookQueueListenerTest.javaリンク先の内容 に変更します。

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

    テストが成功することが確認できます。

    f:id:ksby:20151107072008p:plain

LendingUserDetailsHelper クラスのテストの作成

  1. src/main/java/ksbysample/webapp/lending/security の下の LendingUserDetailsHelper.java で「Create Test」ダイアログを表示し、テストクラスを作成します。

    f:id:ksby:20151107082321p:plain

    src/test/java/ksbysample/webapp/lending/security の下に LendingUserDetailsHelperTest.java が作成されます。

  2. src/test/java/ksbysample/webapp/lending/security の下の LendingUserDetailsHelperTest.javaリンク先の内容 に変更します。

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

    テストが成功することが確認できます。

    f:id:ksby:20151107083634p:plain

UploadBooklistFormValidator クラスのテストの作成

  1. src/main/java/ksbysample/webapp/lending/web/booklist の下の UploadBooklistFormValidator.java で「Create Test」ダイアログを表示し、テストクラスを作成します。

    f:id:ksby:20151107084102p:plain

    src/test/java/ksbysample/webapp/lending/web/booklist の下に UploadBooklistFormValidatorTest.java が作成されます。

  2. src/test/java/ksbysample/webapp/web/booklist の下の UploadBooklistFormValidatorTest.javaリンク先の内容 に変更します。

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

    テストが成功することが確認できます。

    f:id:ksby:20151107090118p:plain

全てのテストが成功するか確認する

  1. 最後に全てのテストが成功するか確認します。Project View のルートでコンテキストメニューを表示して「Run 'Tests in 'ksbysample...' with Coverage」を選択します。

    テストが実行され、全て成功することが確認できます。

    f:id:ksby:20151107190742p:plain

  2. clean タスクの実行→「Rebuild Project」メニューの実行→build タスクの実行を行い、"BUILD SUCCESSFUL" のメッセージが出力されることも確認します。

    f:id:ksby:20151107191448p:plain

commit、Push、Pull Request、マージ

  1. ここまでの変更内容を commit します。

  2. コマンドラインから以下のコマンドを実行して commit を1つにまとめます。

    > git rebase -i HEAD~3
    > git commit --amend -m "#37 貸出状況取得タスクを作成しました。"

  3. GitHub へ Push、feature/37-issue -> 1.0.x へ Pull Request、1.0.x でマージ、feature/37-issue ブランチを削除、をします。

次回は。。。

番外編としてテストクラス内で @Autowired で DI したクラスの中の @Autowired で DI しているフィールドを一部だけ JMockit でモック化する方法をまとめます ( 今回これをやろうとしてかなり試行錯誤しました。。。 )。

その後で IntelliJ IDEA Ultimate Edition を 15.0 へバージョンアップしてから、 次の貸出申請画面の作成に進みます。

メモ書き

  • IntelliJ IDEA Ulitimate Edition の Database tools で出力する CSV ファイルには NULL は "NULL" と出力されることに今回テストデータを作成していて気付きました。TestDataResource や TestDataLoaderResource で NULL に変換する文字列を "[null]" にしていますが、"NULL" にした方がよさそうです。ただし ksbysample-webapp-lending ではこのまま進めます。

  • InquiringStatusOfBookQueueListenerTest のテストを作成している時に気付きました。自作したテスト検証用クラス TableDataAssert の assertEquals メソッドの2番目の引数には検証対象外のカラムを列挙するよう実装しているのですが、今回のテストのように1カラムだけ検証したい場合には検証対象外のカラムを列挙するのはちょっと手間で、検証したいカラムだけ指定できるようにできないかと思って再度見直したところ DefaultColumnFilter クラスに excludedColumnsTable メソッド以外に includedColumnsTable メソッドも存在していました。近いうちに TableDataAssert クラスを修正して、検証対象のカラムだけ指定できるメソッドも用意したいと思います。

  • テストデータの作成・配置方法や、テストで使用する共通クラスの作成方法のルールを決めておきたいと思いました。テストデータを作成する必要があるけどどこに作ろうかとか、あるテストを書いていると別のテストで作成したメソッドを利用したいとか、今回特に強く思いました。こちらも近いうちに1度考えてみたいと思います。

ソースコード

build.gradle

task printClassWhatNotMakeTest << {
    def srcDir = new File("src/main/java");
    def excludePaths = [
            "src/main/java/ksbysample/webapp/lending/Application.java"
            , "src/main/java/ksbysample/webapp/lending/config"
            , "src/main/java/ksbysample/webapp/lending/dao"
            , "src/main/java/ksbysample/webapp/lending/entity"
            , "src/main/java/ksbysample/webapp/lending/exception"
            , "src/main/java/ksbysample/webapp/lending/helper/page/PagenationHelper.java"
            , "src/main/java/ksbysample/webapp/lending/security/LendingUser.java"
            , "src/main/java/ksbysample/webapp/lending/security/RoleAwareAuthenticationSuccessHandler.java"
            , "src/main/java/ksbysample/webapp/lending/service/calilapi/response"
            , "src/main/java/ksbysample/webapp/lending/service/file/BooklistCSVRecord.java"
            , "src/main/java/ksbysample/webapp/lending/service/openweathermapapi"
            , "src/main/java/ksbysample/webapp/lending/service/queue/InquiringStatusOfBookQueueMessage.java"
            , "src/main/java/ksbysample/webapp/lending/util/doma"
            , "src/main/java/ksbysample/webapp/lending/util/velocity/VelocityUtils.java"
            , "src/main/java/ksbysample/webapp/lending/webapi/common/CommonWebApiResponse.java"
            , "src/main/java/ksbysample/webapp/lending/webapi/weather"
    ];
    def excludeFileNamePatterns = [
            ".*EventListener.java"
            , ".*Form.java"
            , ".*Values.java"
    ];

    compareSrcAndTestDir(srcDir, excludePaths, excludeFileNamePatterns);
}
  • , "src/main/java/ksbysample/webapp/lending/service/calilapi/Librar", "src/main/java/ksbysample/webapp/lending/service/calilapi/response" へ変更します。

EmailServiceTest.java

package ksbysample.webapp.lending.service;

import ksbysample.common.test.MailServerResource;
import ksbysample.webapp.lending.Application;
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.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;

import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;

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

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

    @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("これはテストです"));
    }

}

lending/helper/mail/assertdata/001/message.txt

貸出状況を確認しました。以下のURLから借りたい書籍を申請してください。

http://localhost:8080/lendingapp?lendingAppId=1

Mail001HelperTest.java

package ksbysample.webapp.lending.helper.mail;

import com.google.common.base.Charsets;
import ksbysample.webapp.lending.Application;
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 javax.mail.Message;
import javax.mail.internet.MimeMessage;
import java.io.File;

import static org.assertj.core.api.Assertions.assertThat;

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

    @Autowired
    private Mail001Helper mail001Helper;

    @Test
    public void testCreateMessage() throws Exception {
        MimeMessage message = mail001Helper.createMessage("test@sample.com", 1L);
        assertThat(message.getRecipients(Message.RecipientType.TO))
                .extracting(Object::toString)
                .containsOnly("test@sample.com");
        assertThat(message.getContent())
                .isEqualTo(com.google.common.io.Files.toString(
                        new File("src/test/resources/ksbysample/webapp/lending/helper/mail/assertdata/001/message.txt")
                        , Charsets.UTF_8));
    }
}
  • To アドレスのチェックにおいて message.getRecipients(Message.RecipientType.TO) は Address[] のデータを返すので、AssertJ の extracting メソッドを使用して String へ変換してからチェックしています。
  • 本文のチェックはテストデータを別ファイルに用意しておいて Guava の Files.toString を使用して String のデータを取得してチェックしています。

listener/rabbitmq/testdata/001/table-ordering.txt, lending_app.csv, lending_book.csv

■table-ordering.txt

lending_app
lending_book

■lending_app.csv

lending_app_id,status,lending_user_id,approval_user_id
1,1,1,[null]

■lending_book.csv

lending_book_id,lending_app_id,isbn,book_name,lending_state,lending_app_flg,lending_app_reason,approval_result,approval_reason
1,1,978-4-7741-6366-6,GitHub実践入門,[null],[null],[null],[null],[null]
2,1,978-4-7741-5377-3,JUnit実践入門,[null],[null],[null],[null],[null]
3,1,978-4-7973-8014-9,Java最強リファレンス,[null],[null],[null],[null],[null]
4,1,978-4-7973-4778-4,アジャイルソフトウェア開発の奥義,[null],[null],[null],[null],[null]
5,1,978-4-87311-704-1,Javaによる関数型プログラミング,[null],[null],[null],[null],[null]

listener/rabbitmq/assertdata/001/table-ordering.txt, lending_app.csv, lending_book.csv

■table-ordering.txt

lending_app
lending_book

■lending_app.csv

lending_app_id,status,lending_user_id,approval_user_id
1,2,1,[null]

■lending_book.csv

lending_book_id,lending_app_id,isbn,book_name,lending_state,lending_app_flg,lending_app_reason,approval_result,approval_reason
1,1,978-4-7741-6366-6,GitHub実践入門,貸出可,[null],[null],[null],[null]
2,1,978-4-7741-5377-3,JUnit実践入門,蔵書あり,[null],[null],[null],[null]
3,1,978-4-7973-8014-9,Java最強リファレンス,貸出中,[null],[null],[null],[null]
4,1,978-4-7973-4778-4,アジャイルソフトウェア開発の奥義,準備中,[null],[null],[null],[null]
5,1,978-4-87311-704-1,Javaによる関数型プログラミング,蔵書なし,[null],[null],[null],[null]

Book.java, SystemData.java, Libkey.java

■Book.java

@JsonIgnoreProperties(ignoreUnknown = true)
@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class Book {

■SystemData.java

@JsonIgnoreProperties(ignoreUnknown = true)
@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class SystemData {

■Libkey.java

@JsonIgnoreProperties(ignoreUnknown = true)
@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class Libkey {

InquiringStatusOfBookQueueListenerTest.java

package ksbysample.webapp.lending.listener.rabbitmq;

import com.google.common.base.Charsets;
import ksbysample.common.test.*;
import ksbysample.webapp.lending.Application;
import ksbysample.webapp.lending.dao.LibraryForsearchDao;
import ksbysample.webapp.lending.entity.LibraryForsearch;
import ksbysample.webapp.lending.service.calilapi.CalilApiService;
import ksbysample.webapp.lending.service.calilapi.response.Book;
import ksbysample.webapp.lending.service.calilapi.response.Libkey;
import ksbysample.webapp.lending.service.calilapi.response.SystemData;
import ksbysample.webapp.lending.service.queue.InquiringStatusOfBookQueueMessage;
import mockit.Mock;
import mockit.MockUp;
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.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.support.converter.MessageConverter;
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 javax.mail.internet.MimeMessage;
import javax.sql.DataSource;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import static mockit.Deencapsulation.getField;
import static mockit.Deencapsulation.setField;
import static org.assertj.core.api.Assertions.assertThat;

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

    @Rule
    @Autowired
    public TestDataResource testDataResource;

    @Rule
    @Autowired
    public TestDataLoaderResource testDataLoaderResource;

    @Rule
    @Autowired
    public MailServerResource mailServer;

    @Autowired
    private DataSource dataSource;

    @Autowired
    private MessageConverter messageConverter;

    @Autowired
    private InquiringStatusOfBookQueueListener listener;

    @Test
    @TestDataLoader("src/test/resources/ksbysample/webapp/lending/listener/rabbitmq/testdata/001")
    public void testReceiveMessage() throws Exception {
        // モックに入れ替える前のフィールドの実体を退避する
        LibraryForsearchDao libraryForsearchDaoOrg = getField(listener, "libraryForsearchDao");
        CalilApiService calilApiServiceOrg = getField(listener, "calilApiService");
        try {
            /**
             * モック定義部
             */
            // InquiringStatusOfBookQueueListener.libraryForsearchDao をモックに入れ替える
            LibraryForsearchDao libraryForsearchDao = new MockUp<LibraryForsearchDao>() {
                @Mock
                LibraryForsearch selectSelectedLibrary() {
                    LibraryForsearch libraryForsearch = new LibraryForsearch();
                    libraryForsearch.setSystemid("System_Id");
                    libraryForsearch.setFormal("図書館名");
                    return libraryForsearch;
                }
            }.getMockInstance();
            setField(listener, "libraryForsearchDao", libraryForsearchDao);

            // InquiringStatusOfBookQueueListener.calilApiService をモックに入れ替える
            CalilApiService calilApiService = new MockUp<CalilApiService>() {
                @Mock
                public List<Book> check(String systemid, List<String> isbnList) {
                    List<Book> bookList = new ArrayList<>();
                    bookList.add(new Book("978-4-7741-6366-6", null, new SystemData(null, null, null, Arrays.asList(new Libkey(null, "貸出可")))));
                    bookList.add(new Book("978-4-7741-5377-3", null, new SystemData(null, null, null, Arrays.asList(new Libkey(null, "蔵書あり")))));
                    bookList.add(new Book("978-4-7973-8014-9", null, new SystemData(null, null, null, Arrays.asList(new Libkey(null, "貸出中")))));
                    bookList.add(new Book("978-4-7973-4778-4", null, new SystemData(null, null, null, Arrays.asList(new Libkey(null, "準備中")))));
                    bookList.add(new Book("978-4-87311-704-1", null, new SystemData(null, null, null, Arrays.asList(new Libkey(null, "蔵書なし")))));
                    return bookList;
                }
            }.getMockInstance();
            setField(listener, "calilApiService", calilApiService);

            /**
             * テスト本体
             */
            InquiringStatusOfBookQueueMessage queueMessage = new InquiringStatusOfBookQueueMessage();
            queueMessage.setLendingAppId(1L);
            Message message = messageConverter.toMessage(queueMessage, new MessageProperties());
            listener.receiveMessage(message);

            /**
             * 検証
             */
            // テーブルの以下のカラムのデータを検証する
            //  ・lending_app.status
            //  ・lending_book.lending_state
            IDataSet dataSet = new CsvDataSet(new File("src/test/resources/ksbysample/webapp/lending/listener/rabbitmq/assertdata/001"));
            TableDataAssert tableDataAssert = new TableDataAssert(dataSet, dataSource);
            tableDataAssert.assertEquals("lending_app", new String[]{"lending_app_id", "lending_user_id", "approval_user_id"});
            tableDataAssert.assertEquals("lending_book", new String[]{"lending_book_id", "lending_app_id", "isbn", "book_name", "lending_app_flg", "lending_app_reason", "approval_result", "approval_reason"});

            // 送信されたメールを検証する
            assertThat(mailServer.getMessagesCount()).isEqualTo(1);
            MimeMessage mimeMessage = mailServer.getFirstMessage();
            assertThat(mimeMessage.getRecipients(javax.mail.Message.RecipientType.TO))
                    .extracting(Object::toString)
                    .containsOnly("tanaka.taro@sample.com");
            assertThat(mimeMessage.getContent())
                    .isEqualTo(com.google.common.io.Files.toString(
                            new File("src/test/resources/ksbysample/webapp/lending/helper/mail/assertdata/001/message.txt")
                            , Charsets.UTF_8));
        } finally {
            // モックに差し替えたフィールドを退避しておいた元の実体に戻す
            setField(listener, "libraryForsearchDao", libraryForsearchDaoOrg);
            setField(listener, "calilApiService", calilApiServiceOrg);
        }
    }

}
  • 今回のテストでは、InquiringStatusOfBookQueueListener クラスの receiveMessage メソッド内の以下の処理のみモックに入れ替えています。
    • libraryForsearchDao.selectSelectedLibrary()
    • calilApiService.check(...)

LendingUserDetailsHelperTest.java

package ksbysample.webapp.lending.security;

import ksbysample.common.test.TestDataResource;
import ksbysample.webapp.lending.Application;
import ksbysample.webapp.lending.dao.UserInfoDao;
import ksbysample.webapp.lending.entity.UserInfo;
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.security.authentication.TestingAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;

import static org.assertj.core.api.Assertions.assertThat;

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

    @Rule
    @Autowired
    public TestDataResource testDataResource;

    @Autowired
    private UserInfoDao userInfoDao;
    
    @Test
    public void testGetLoginUserId() throws Exception {
        UserInfo userInfo = userInfoDao.selectById(1L);
        LendingUserDetails lendingUserDetails = new LendingUserDetails(userInfo, null);
        Authentication auth = new TestingAuthenticationToken(lendingUserDetails, null);
        SecurityContextHolder.getContext().setAuthentication(auth);
        
        assertThat(LendingUserDetailsHelper.getLoginUserId()).isEqualTo(1L);
    }
}

UploadBooklistFormValidatorTest.java

package ksbysample.webapp.lending.web.booklist;

import com.univocity.parsers.csv.CsvWriter;
import com.univocity.parsers.csv.CsvWriterSettings;
import ksbysample.webapp.lending.Application;
import ksbysample.webapp.lending.service.file.BooklistCsvFileServiceTest;
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.mock.web.MockMultipartFile;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.validation.Errors;
import org.springframework.validation.MapBindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.multipart.MultipartFile;

import java.io.BufferedWriter;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.*;

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

    @Autowired
    private UploadBooklistFormValidator uploadBooklistFormValidator;
    
    private BooklistCsvFileServiceTest booklistCsvFileServiceTest = new BooklistCsvFileServiceTest();
    
    @Test
    public void testValidate_NoErrorCsvFile() throws Exception {
        UploadBooklistForm uploadBooklistForm = new UploadBooklistForm();
        MultipartFile multipartFile = booklistCsvFileServiceTest.createNoErrorCsvFile();
        uploadBooklistForm.setFileupload(multipartFile);
        Errors errors = new MapBindingResult(new HashMap<String, String>(), "");
        uploadBooklistFormValidator.validate(uploadBooklistForm, errors);
        assertThat(errors.hasErrors()).isFalse();
    }

    @Test
    public void testValidate_ErrorCsvFile() throws Exception {
        UploadBooklistForm uploadBooklistForm = new UploadBooklistForm();
        MultipartFile multipartFile = booklistCsvFileServiceTest.createErrorCsvFile();
        uploadBooklistForm.setFileupload(multipartFile);
        Errors errors = new MapBindingResult(new HashMap<String, String>(), "");
        uploadBooklistFormValidator.validate(uploadBooklistForm, errors);
        assertThat(errors.hasErrors()).isTrue();
        assertThat(errors.getErrorCount()).isEqualTo(6);
        assertThat(errors.getAllErrors())
                .contains(new ObjectError("", new String[]{"UploadBooklistForm.fileupload.lengtherr"}, new Object[]{2, 3}, null))
                .contains(new ObjectError("", new String[]{"UploadBooklistForm.fileupload.isbn.patternerr"}, new Object[]{3, "978-4-7741-5x77-3"}, null))
                .contains(new ObjectError("", new String[]{"UploadBooklistForm.fileupload.isbn.lengtherr"}, new Object[]{4, "978-4-79173-8014-9"}, null))
                .contains(new ObjectError("", new String[]{"UploadBooklistForm.fileupload.isbn.numlengtherr"}, new Object[]{4, "97847917380149"}, null))
                .contains(new ObjectError("", new String[]{"UploadBooklistForm.fileupload.isbn.numlengtherr"}, new Object[]{5, "97847197347784"}, null))
                .contains(new ObjectError("", new String[]{"UploadBooklistForm.fileupload.bookname.lengtherr"}, new Object[]{6, "123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"}, null));
    }

}
  • テストで使用する CSV ファイルの作成には BooklistCsvFileServiceTest クラスのメソッドを利用します ( 本来は共通クラスに CSV ファイル生成用のメソッドを移動した方がよいと思いますが今回はこれで進めます )。

履歴

2015/11/07
初版発行。
2015/11/08
* ファイルアップロード時にエラーが出ることに気づいたため、Book, SystemData, Libkey クラスに @AllArgsConstructor 以外に @NoArgsConstructor も付加するよう修正しました。