かんがるーさんの日記

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

Spring Boot で書籍の貸出状況確認・貸出申請する Web アプリケーションを作る ( その26 )( 貸出希望書籍 CSV ファイルアップロード画面の作成5 )

概要

Spring Boot で書籍の貸出状況確認・貸出申請する Web アプリケーションを作る ( その25 )( 貸出希望書籍 CSV ファイルアップロード画面の作成4 ) の続きです。

  • 今回の手順で確認できるのは以下の内容です。
    • 貸出希望書籍 CSV ファイルアップロード画面の作成
      • テストの作成の続きです。

参照したサイト・書籍

  1. spring-amqp/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/
    https://github.com/spring-projects/spring-amqp/tree/master/spring-rabbit/src/test/java/org/springframework/amqp/rabbit

    • Spring AMQP の RabbitMQ 用ライブラリの GitHub にある test ディレクトリです。Spring AMQP + RabbitMQ のテストの方法が分からなかったので参考にしました。
  2. Java Code Examples for org.springframework.amqp.rabbit.core.RabbitAdmin
    http://www.programcreek.com/java-api-examples/index.php?api=org.springframework.amqp.rabbit.core.RabbitAdmin

    • Spring AMQP + RabbitMQ のテストの方法についてこちらのサイトも参考にしました。
    • このサイトはサンプルが豊富な気がします。最近よく見に行きます。
  3. The JMockit Testing Toolkit Tutorial
    http://jmockit.org/tutorial.html

  4. Java Code Examples for org.springframework.test.web.servlet.request.MockMvcRequestBuilders
    http://www.programcreek.com/java-api-examples/index.php?api=org.springframework.test.web.servlet.request.MockMvcRequestBuilders

    • MockMvcRequestBuilders によるファイルアップロードのテストの書き方を参照しました。Example 8 です。

目次

  1. InquiringStatusOfBookQueueService クラスのテストの作成
  2. BooklistService クラスのテストの作成
  3. BooklistController クラスのテストの作成
  4. 全てのテストが成功するか確認する
  5. commit、Push、Pull Request、マージ
  6. 次回は。。。
  7. メモ書き
    1. @Autowired で DI したインスタンスの private メソッドの処理を Mock に出来るのか?

手順

InquiringStatusOfBookQueueService クラスのテストの作成

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

    f:id:ksby:20151010074757p:plain

    src/test/java/ksbysample/webapp/lending/service/queue の下に InquiringStatusOfBookQueueServiceTest.java が作成されます。

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

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

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

    f:id:ksby:20151010110845p:plain

BooklistService クラスのテストの作成

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

    f:id:ksby:20151010111913p:plain

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

  2. lending_app, lending_book テーブルをテストデータに入れ替えるための記述を table-ordering.txt に記述するのを忘れていたので追加します。src/test/resources/testdata/base の下の table-ordering.txt を リンク先の内容 に変更します。

    1点注意事項として table-ordering.txt は外部キー制約で親子関係があるテーブルは子テーブルを下に書くようにします。そうしないとテーブルのデータを削除することができずテスト実行時にエラーが発生します。

  3. アップロードファイルの MockMultipartFile クラスのインスタンスを生成するために BooklistCsvFileServiceTest のメソッドを利用できるようにします。src/test/java/ksbysample/webapp/lending/service/file の下の BooklistCsvFileServiceTest.javaリンク先の内容 に変更します。

  4. 今のままだとテストしにくいことに気づいたのでテストしやすい構成に変更します。まず src/main/java/ksbysample/webapp/lending/security の下に LendingUserDetailsHelper.java を作成します。作成後、リンク先の内容 に変更します。

  5. 次に src/main/java/ksbysample/webapp/lending/web/booklist の下の BooklistService.javaリンク先のその1の内容 に変更します。

  6. データ登録後の確認用データを作成します。src/test/resources/ksbysample/webapp/lending/web の下に booklist/assertdata/001 ディレクトリを作成します。

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

  8. BooklistController クラス、BooklistService クラスで実装時にメソッド名と処理内容を2転、3転させていて修正し忘れていた部分があることに気づいたので修正します。src/main/java/ksbysample/webapp/lending/web/booklist の下の BooklistService.javaリンク先のその2の内容 に変更します。

  9. src/main/java/ksbysample/webapp/lending/web/booklist の下の BooklistController.javaリンク先の内容 に変更します。

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

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

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

    f:id:ksby:20151011022029p:plain

BooklistController クラスのテストの作成

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

    f:id:ksby:20151011022909p:plain

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

  2. 最初にテストの構成を決めます。src/test/java/ksbysample/webapp/lending/web/booklist の下の BooklistControllerTest.javaリンク先のその1の内容 に変更します。

  3. テストを実装します。src/test/java/ksbysample/webapp/lending/web/booklist の下の BooklistControllerTest.javaリンク先のその2の内容 に変更します。

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

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

    f:id:ksby:20151012061706p:plain

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

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

    OpenWeatherMapApiServiceTest だけエラーになりました。ログを見ると "401 Unauthorized" と出力されているので、どうも認証が必要になったようです。

    f:id:ksby:20151012070706p:plain

    http://openweathermap.org/api を見ると "To access the API you need to sign up for an API key if you are on a free or paid plan." と書かれていました。特にメインの機能とは関係がないので、今回はテストを実行対象外にします。

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

  3. 再度全てのテストを実行し、今度は成功することが確認できます。

    f:id:ksby:20151012072243p:plain

    ※UniVocityParsersTest はライブラリの検証のために作成したテストなので、こちらも対象外にしました。Qiita に記事を書いたので、詳細は Java の CSV/TSV/固定長ファイル Parser ライブラリ uniVocity-parsers の使い方 をご覧ください。

    ※"(Coverage): Error during class instrumentation: org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory: java.lang.NullPointerException" というログが出ており、stackoverflow の Error trying to wire a Lettuce Connection Factory の記事を参考にすれば解決するような気がしますが、一旦無視します。

commit、Push、Pull Request、マージ

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

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

    > git rebase -i HEAD~8
    > git commit --amend -m "#29 貸出希望書籍 CSV ファイルアップロード画面を作成しました。"

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

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

    f:id:ksby:20151012075218p:plain

次回は。。。

最近開発で使用するために書いていた記事を読み返していて、以下の2点が分かりづらかったので改めてまとめ直してみます。

  • Spring Boot で Doma 2 を使用するには
  • 自作したテスト用クラス ( src/test/java/ksbysample/common/test ) の使い方

その後で Git for Windows を 2.6.1 にバージョンアップした後、次の貸出状況取得タスクの作成に進みます。

メモ書き

@Autowired で DI したインスタンスの private メソッドの処理だけを JMockit で Mock に出来るのか?

結論として、出来ます。ただし上でテストを作っている時には分かりませんでした。

BooklistService クラスの temporarySaveBookListCsvFile メソッドの中で現在ログイン中のユーザの userId を取得する処理を、ヘルパークラスの LendingUserDetailsHelper.getLoginUserId() ではなく、BooklistService クラス内に getLoginUserId という private メソッドを用意して、BooklistServiceTest で以下のように実装して getLoginUserId メソッドの処理だけ Mock にしようと最初思っていたのですが、この方法ではダメでした。

    @Autowired
    private BooklistService booklistService;
    
    @Test
    public void testTemporarySaveBookListCsvFile() throws Exception {
        new NonStrictExpectations(booklistService) {{
            invoke(booklistService, "getLoginUserId"); result = new Long(1);
        }};

invoke を呼び出すと booklistService のインスタンスが Mock に変わり、temporarySaveBookListCsvFile メソッドまで Mock メソッドになって実際の処理が呼び出されません。temporarySaveBookListCsvFile メソッド内にブレークポイントを設定してデバッグしても通過しませんでした。

JMockit の Tutorial の Mocking の方ばかり見ていたので正しい方法に気づけなかったのですが、Faking の方を見てやり方が分かりました。

以下の実装で private メソッドのみ Mock メソッドにできます。

    @Autowired
    private BooklistService booklistService;

    public class MockBooklistService extends MockUp<BooklistService> {
        @Mock
        private Long getLoginUserId() {
            return new Long(1);
        }
    }
    
    @Test
    public void testTemporarySaveBookListCsvFile() throws Exception {
        new MockBooklistService();

        UploadBooklistForm uploadBooklistForm = new UploadBooklistForm();

ポイントは以下の通りです。

ただしまだよく分からないことがあります。上の方法で getLoginUserId メソッドだけ Mock メソッドにしてそれ以外は本来のメソッドが呼び出されているはずなのですが、BooklistService::temporarySaveBookListCsvFile の中にブレークポイントを設定してテストメソッドを Debug モードで実行してもなぜかブレークポイントを通過しない ( 止まらない ) んですよね。まだまだ分からないことがあるんだな。。。 と思いました。

ソースコード

InquiringStatusOfBookQueueServiceTest.java

package ksbysample.webapp.lending.service.queue;

import ksbysample.webapp.lending.Application;
import ksbysample.webapp.lending.config.Constant;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
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 static org.assertj.core.api.Assertions.assertThat;

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

    @Autowired
    private ConnectionFactory connectionFactory;

    @Autowired
    private Queue queue;
    
    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private InquiringStatusOfBookQueueService inquiringStatusOfBookQueueService;
    
    @Test
    public void testSendMessage() throws Exception {
        RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory);
        rabbitAdmin.deleteQueue(Constant.QUEUE_NAME_INQUIRING_STATUSOFBOOK);
        rabbitAdmin.declareQueue(queue);

        Long lendingAppId = new Long(1);
        inquiringStatusOfBookQueueService.sendMessage(lendingAppId);

        InquiringStatusOfBookQueueMessage message
                = (InquiringStatusOfBookQueueMessage) rabbitTemplate.receiveAndConvert(Constant.QUEUE_NAME_INQUIRING_STATUSOFBOOK);
        assertThat(message.getLendingAppId()).isEqualTo(lendingAppId);
    }
}

table-ordering.txt

user_info
user_role
library_forsearch
lending_app
lending_book
  • lending_app, lending_book を追加します。

BooklistCsvFileServiceTest.java

    public MockMultipartFile createNoErrorCsvFile() throws Exception {
        .....
    }

    public MockMultipartFile createErrorCsvFile() throws Exception {
        .....
    }
  • createNoErrorCsvFile, createErrorCsvFile メソッドを他のテストクラスからも利用したいので private → public へ変更します。

LendingUserDetailsHelper.java

package ksbysample.webapp.lending.security;

import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;

public class LendingUserDetailsHelper {

    /**
     * 現在ログインしているユーザのユーザIDを取得する
     * 
     * @return ユーザID(user_info.user_id)
     */
    public static Long getLoginUserId() {
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        LendingUserDetails lendingUserDetails = (LendingUserDetails) auth.getPrincipal();
        return lendingUserDetails.getUserId();
    }

}

BooklistService.java

■その1

    public Long temporarySaveBookListCsvFile(UploadBooklistForm uploadBooklistForm) {
        // アップロードされたCSVファイルのデータを List に変換する
        List<BooklistCSVRecord> booklistCSVRecordList
                = booklistCsvFileService.convertFileToList(uploadBooklistForm.getFileupload());
        
        // lending_app テーブルにデータを保存する
        LendingApp lendingApp = new LendingApp();
        lendingApp.setStatus(TENPORARY_SAVE.getValue());
        lendingApp.setLendingUserId(LendingUserDetailsHelper.getLoginUserId());
        lendingAppDao.insert(lendingApp);

        // lending_book テーブルにデータを保存する
        LendingBook lendingBook;
        for (BooklistCSVRecord booklistCSVRecord : booklistCSVRecordList) {
            lendingBook = new LendingBook();
            BeanUtils.copyProperties(booklistCSVRecord, lendingBook);
            lendingBook.setLendingAppId(lendingApp.getLendingAppId());
            lendingBookDao.insert(lendingBook);
        }

        return lendingApp.getLendingAppId();
    }
  • temporarySaveBookListCsvFile メソッドの以下の点を変更します。テスト時に Mock にしたい部分を別クラスのメソッドを呼び出すようにします。
    • SecurityContextHolder を利用して LendingUserDetails のインスタンスを取得していた部分を削除します。
    • lendingApp.setLendingUserId(lendingUserDetails.getUserId());lendingApp.setLendingUserId(LendingUserDetailsHelper.getLoginUserId()); へ変更します。

■その2

    public void sendMessageToInquiringStatusOfBookQueue(RegisterBooklistForm registerBooklistForm) {
        inquiringStatusOfBookQueueService.sendMessage(registerBooklistForm.getLendingAppId());
    }
  • メソッド名を updateLendingAppStatusToPending → sendMessageToInquiringStatusOfBookQueue へ変更します。

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
1,1,978-4-7741-5377-3,JUnit実践入門,NULL,NULL,NULL,NULL,NULL
1,1,978-4-7973-8014-9,Java最強リファレンス,NULL,NULL,NULL,NULL,NULL
1,1,978-4-7973-4778-4,アジャイルソフトウェア開発の奥義,NULL,NULL,NULL,NULL,NULL
1,1,978-4-87311-704-1,Javaによる関数型プログラミング,NULL,NULL,NULL,NULL,NULL

BooklistController.java

    @RequestMapping("/register")
    public String register(RegisterBooklistForm registerBooklistForm
            , RedirectAttributes redirectAttributes) {
        booklistService.sendMessageToInquiringStatusOfBookQueue(registerBooklistForm);
        redirectAttributes.addFlashAttribute("lendingAppId", registerBooklistForm.getLendingAppId());
        return "redirect:/booklist/complete";
    }
  • booklistService.updateLendingAppStatusToPending(registerBooklistForm);booklistService.sendMessageToInquiringStatusOfBookQueue(registerBooklistForm); へ変更します。

BooklistServiceTest.java

package ksbysample.webapp.lending.web.booklist;

import ksbysample.common.test.TableDataAssert;
import ksbysample.common.test.TestDataResource;
import ksbysample.webapp.lending.Application;
import ksbysample.webapp.lending.entity.LendingBook;
import ksbysample.webapp.lending.security.LendingUserDetailsHelper;
import ksbysample.webapp.lending.service.file.BooklistCsvFileServiceTest;
import mockit.NonStrictExpectations;
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 javax.sql.DataSource;
import java.io.File;
import java.util.List;

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

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

    private final String MAILADDR_TANAKA_TARO = "tanaka.taro@sample.com";

    @Rule
    @Autowired
    public TestDataResource testDataResource;

    @Autowired
    private DataSource dataSource;
    
    @Autowired
    private BooklistService booklistService;
    
    @Test
    public void testTemporarySaveBookListCsvFile() throws Exception {
        new NonStrictExpectations(LendingUserDetailsHelper.class) {{
            LendingUserDetailsHelper.getLoginUserId(); result = new Long(1);
        }};

        UploadBooklistForm uploadBooklistForm = new UploadBooklistForm();
        // テスト用のユーティリティクラスを作るべきですが、今回は他のテストクラスのメソッドをそのまま使います
        BooklistCsvFileServiceTest booklistCsvFileServiceTest = new BooklistCsvFileServiceTest();
        uploadBooklistForm.setFileupload(booklistCsvFileServiceTest.createNoErrorCsvFile());
        
        Long lendingAppId = booklistService.temporarySaveBookListCsvFile(uploadBooklistForm);
        assertThat(lendingAppId).isNotEqualTo(new Long(0));
        IDataSet dataSet = new CsvDataSet(new File("src/test/resources/ksbysample/webapp/lending/web/booklist/assertdata/001"));
        TableDataAssert tableDataAssert = new TableDataAssert(dataSet, dataSource);
        tableDataAssert.assertEquals("lending_app", new String[]{"lending_app_id", "approval_user_id"});
        tableDataAssert.assertEquals("lending_book", new String[]{"lending_book_id", "lending_app_id", "lending_state", "lending_app_flg", "lending_app_reason", "approval_result", "approval_reason"});

        List<LendingBook> lendingBookList = booklistService.getLendingBookList(lendingAppId);
        assertThat(lendingBookList).hasSize(5);
    }

    @Test
    public void testSendMessageToInquiringStatusOfBookQueue() throws Exception {
        // 現在の実装では InquiringStatusOfBookQueueServiceTest が通ればOKなので、こちらは実装しない
    }

}
  • BooklistService クラスの temporarySaveBookListCsvFile メソッドと getLendingBookList メソッドは1つのテストメソッド内でテストした方が効率的なので、別々に分けずにまとめます。
  • testSendMessageToInquiringStatusOfBookQueue テストメソッドは InquiringStatusOfBookQueueServiceTest の testSendMessage テストメソッドでテストしたい内容がカバーできているので、実装しません。

BooklistControllerTest.java

■その1

package ksbysample.webapp.lending.web.booklist;

import ksbysample.webapp.lending.Application;
import org.junit.Test;
import org.junit.experimental.runners.Enclosed;
import org.junit.runner.RunWith;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;

@RunWith(Enclosed.class)
public class BooklistControllerTest {

    @RunWith(SpringJUnit4ClassRunner.class)
    @SpringApplicationConfiguration(classes = Application.class)
    @WebAppConfiguration
    public static class ユーザ権限とURLの呼び出し可否のテスト {

        @Test
        public void ログインしていなければ貸出希望書籍CSVファイルアップロード画面は表示できない() throws Exception {
        }

        @Test
        public void 一般ユーザ権限を持つユーザは貸出希望書籍CSVファイルアップロード画面を表示できる() throws Exception {
        }

        @Test
        public void ログインしていなければアップロード用のURLは呼び出せない() throws Exception {
        }

        @Test
        public void ログインしていなければ確認画面の登録ボタン用のURLは呼び出せない() throws Exception {
        }

    }

    @RunWith(SpringJUnit4ClassRunner.class)
    @SpringApplicationConfiguration(classes = Application.class)
    @WebAppConfiguration
    public static class ファイルアップロードのテスト_エラーになる場合 {

        @Test
        public void エラーが出るCSVファイルをアップロードするとアップロード画面にエラーメッセージを表示する() throws Exception {
        }

    }

    @RunWith(SpringJUnit4ClassRunner.class)
    @SpringApplicationConfiguration(classes = Application.class)
    @WebAppConfiguration
    public static class ファイルアップロードのテスト_正常な場合 {

        @Test
        public void エラーのないCSVファイルをアップロードしてから確認画面で登録ボタンをクリックすると完了画面が表示される() throws Exception {
        }

    }

}

■その2

package ksbysample.webapp.lending.web.booklist;

import ksbysample.common.test.SecurityMockMvcResource;
import ksbysample.common.test.TestDataResource;
import ksbysample.webapp.lending.Application;
import ksbysample.webapp.lending.service.file.BooklistCsvFileServiceTest;
import org.junit.Rule;
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.mock.web.MockMultipartFile;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MvcResult;

import static ksbysample.common.test.ErrorsResultMatchers.errors;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

@RunWith(Enclosed.class)
public class BooklistControllerTest {

    @RunWith(SpringJUnit4ClassRunner.class)
    @SpringApplicationConfiguration(classes = Application.class)
    @WebAppConfiguration
    public static class ユーザ権限とURLの呼び出し可否のテスト {

        @Rule
        @Autowired
        public TestDataResource testDataResource;

        @Rule
        @Autowired
        public SecurityMockMvcResource mvc;

        @Test
        public void ログインしていなければ貸出希望書籍CSVファイルアップロード画面は表示できない() throws Exception {
            mvc.noauth.perform(get("/booklist"))
                    .andExpect(status().isFound())
                    .andExpect(redirectedUrl("http://localhost/"));
        }

        @Test
        public void 一般ユーザ権限を持つユーザは貸出希望書籍CSVファイルアップロード画面を表示できる() throws Exception {
            mvc.authSuzukiHanako.perform(get("/booklist"))
                    .andExpect(status().isOk())
                    .andExpect(view().name("booklist/booklist"));
        }

        @Test
        public void ログインしていなければアップロード用のURLは呼び出せない() throws Exception {
            mvc.noauth.perform(post("/booklist/fileupload"))
                    .andExpect(status().isForbidden());
        }

        @Test
        public void ログインしていなければ確認画面の登録ボタン用のURLは呼び出せない() throws Exception {
            mvc.noauth.perform(post("/booklist/register"))
                    .andExpect(status().isForbidden());
        }

    }

    @RunWith(SpringJUnit4ClassRunner.class)
    @SpringApplicationConfiguration(classes = Application.class)
    @WebAppConfiguration
    public static class ファイルアップロードのテスト_エラーになる場合 {

        @Rule
        @Autowired
        public TestDataResource testDataResource;

        @Rule
        @Autowired
        public SecurityMockMvcResource mvc;

        @Test
        public void エラーが出るCSVファイルをアップロードするとアップロード画面にエラーメッセージを表示する() throws Exception {
            BooklistCsvFileServiceTest booklistCsvFileServiceTest = new BooklistCsvFileServiceTest();
            MockMultipartFile multipartFile = booklistCsvFileServiceTest.createErrorCsvFile();
            mvc.authSuzukiHanako.perform(
                    fileUpload("/booklist/fileupload")
                            // 単に .file(multipartFile) ではダメで、
                            // 第1引数に <input type="file" name="..."> の name 属性の文字列を、第2引数にファイルのバイト配列を渡す
                            .file("fileupload", multipartFile.getBytes())
                            .with(csrf()))
                    .andExpect(status().isOk())
                    .andExpect(view().name("booklist/booklist"))
                    .andExpect(model().hasErrors())
                    .andExpect(model().errorCount(6))
                    .andExpect(errors().hasGlobalError("uploadBooklistForm", "UploadBooklistForm.fileupload.lengtherr"))
                    .andExpect(errors().hasGlobalError("uploadBooklistForm", "UploadBooklistForm.fileupload.isbn.patternerr"))
                    .andExpect(errors().hasGlobalError("uploadBooklistForm", "UploadBooklistForm.fileupload.isbn.lengtherr"))
                    .andExpect(errors().hasGlobalError("uploadBooklistForm", "UploadBooklistForm.fileupload.isbn.numlengtherr"))
                    .andExpect(errors().hasGlobalError("uploadBooklistForm", "UploadBooklistForm.fileupload.isbn.numlengtherr"))
                    .andExpect(errors().hasGlobalError("uploadBooklistForm", "UploadBooklistForm.fileupload.bookname.lengtherr"));
        }

    }

    @RunWith(SpringJUnit4ClassRunner.class)
    @SpringApplicationConfiguration(classes = Application.class)
    @WebAppConfiguration
    public static class ファイルアップロードのテスト_正常な場合 {

        @Rule
        @Autowired
        public TestDataResource testDataResource;

        @Rule
        @Autowired
        public SecurityMockMvcResource mvc;

        @Test
        public void エラーのないCSVファイルをアップロードしてから確認画面で登録ボタンをクリックすると完了画面が表示される() throws Exception {
            BooklistCsvFileServiceTest booklistCsvFileServiceTest = new BooklistCsvFileServiceTest();
            MockMultipartFile multipartFile = booklistCsvFileServiceTest.createNoErrorCsvFile();

            // CSVファイルをアップロードする
            MvcResult result = mvc.authSuzukiHanako.perform(
                    fileUpload("/booklist/fileupload")
                            // 単に .file(multipartFile) ではダメで、
                            // 第1引数に <input type="file" name="..."> の name 属性の文字列を、第2引数にファイルのバイト配列を渡す
                            .file("fileupload", multipartFile.getBytes())
                            .with(csrf()))
                    .andExpect(status().isOk())
                    .andExpect(view().name("booklist/fileupload"))
                    .andExpect(model().hasNoErrors())
                    .andReturn();
            RegisterBooklistForm registerBooklistForm
                    = (RegisterBooklistForm) result.getModelAndView().getModel().get("registerBooklistForm");

            // 確認画面で登録ボタンをクリックする
            mvc.authSuzukiHanako.perform(
                    post("/booklist/register")
                            .param("lendingAppId", registerBooklistForm.getLendingAppId().toString())
                            .with(csrf()))
                    .andExpect(status().isFound())
                    .andExpect(redirectedUrl("/booklist/complete"));

            // 完了画面を表示する
            mvc.authSuzukiHanako.perform(get("/booklist/complete"))
                    .andExpect(status().isOk())
                    .andExpect(view().name("booklist/complete"));
        }

    }

}

OpenWeatherMapApiServiceTest.java

@Ignore("アクセスキーを要求されるようになったのでテストを実行対象外にする")
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class)
@WebAppConfiguration
public class OpenWeatherMapApiServiceTest {
  • クラスに @Ignore("アクセスキーを要求されるようになったのでテストを実行対象外にする") を付加します。

履歴

2015/10/12
初版発行。