かんがるーさんの日記

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

Spring Boot 2.0.x の Web アプリを 2.1.x へバージョンアップする ( その11 )( @Rule を使用しているテストを JUnit 5 のテストに書き直す )

概要

記事一覧はこちらです。

Spring Boot 2.0.x の Web アプリを 2.1.x へバージョンアップする ( その10 )( @Rule を使用していないテストを JUnit 5 のテストに書き直す+JUnit 5 の Parallel 実行を試してみる ) の続きです。

  • 今回の手順で確認できるのは以下の内容です。
    • テストクラス内で @Rule アノテーションを付与しているクラスを JUnit 5 の Extension Model として使用できるように書き直してから、@Rule を使用しているテストを JUnit 5 のテストに書き直します。

参照したサイト・書籍

  1. JUnit 5 User Guide - 5. Extension Model
    https://junit.org/junit5/docs/snapshot/user-guide/#extensions

  2. TestWatcher in junit5
    https://stackoverflow.com/questions/49037406/testwatcher-in-junit5

目次

  1. 方針
  2. MailServerResource クラスを JUnit 5 の Extension としても使用できるようにする
  3. SecurityMockMvcResource クラスを JUnit 5 の Extension としても使用できるようにする
  4. 次回へ続く。。。

手順

方針

テストクラス内で @Rule アノテーションを付与しているクラスは以下の3つでした。

  • src/test/java/ksbysample/common/test/rule/mail/MailServerResource.java。ExternalResource クラスを継承して before メソッドと after メソッドを override しています。
  • src/test/java/ksbysample/common/test/rule/mockmvc/SecurityMockMvcResource.java。ExternalResource クラスを継承して before メソッドと after メソッドを override しています。
  • src/test/java/ksbysample/common/test/rule/db/TestDataResource.java。TestWatcher クラスを継承して starting メソッドと finished メソッドを override しています。

これらを以下の方針で変更します。

  • ~Resource というクラス名にしていますが ~Extension というクラス名に変更します。また package を ksbysample.common.test.rule から ksbysample.common.test.extension に変更します。
  • JUnit 4 の @Rule でも JUnit 5 の Extension でもどちらでも使用できるように変更します。

MailServerResource クラスを JUnit 5 の Extension としても使用できるようにする

まずは src/test/java/ksbysample/common/test/rule/mail/MailServerResource.java から。現在以下の実装ですが、

package ksbysample.common.test.rule.mail;

import com.icegreen.greenmail.util.GreenMail;
import com.icegreen.greenmail.util.ServerSetup;
import org.junit.rules.ExternalResource;
import org.springframework.stereotype.Component;

import javax.mail.internet.MimeMessage;
import java.util.Arrays;
import java.util.List;

@Component
public class MailServerResource extends ExternalResource {

    private GreenMail greenMail = new GreenMail(new ServerSetup(25, "localhost", ServerSetup.PROTOCOL_SMTP));

    @Override
    protected void before() {
        greenMail.start();
    }

    @Override
    protected void after() {
        greenMail.stop();
    }

    public int getMessagesCount() {
        return greenMail.getReceivedMessages().length;
    }

    public List<MimeMessage> getMessages() {
        return Arrays.asList(greenMail.getReceivedMessages());
    }

    public MimeMessage getFirstMessage() {
        MimeMessage message = null;
        MimeMessage[] receivedMessages = greenMail.getReceivedMessages();
        if (receivedMessages.length > 0) {
            message = receivedMessages[0];
        }
        return message;
    }

}

これを以下のように変更します。

package ksbysample.common.test.extension.mail;

import com.icegreen.greenmail.util.GreenMail;
import com.icegreen.greenmail.util.ServerSetup;
import org.junit.jupiter.api.extension.AfterEachCallback;
import org.junit.jupiter.api.extension.BeforeEachCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.rules.ExternalResource;
import org.springframework.stereotype.Component;

import javax.mail.internet.MimeMessage;
import java.util.Arrays;
import java.util.List;

@Component
public class MailServerExtension extends ExternalResource
        implements BeforeEachCallback, AfterEachCallback {

    private GreenMail greenMail = new GreenMail(new ServerSetup(25, "localhost", ServerSetup.PROTOCOL_SMTP));

    @Override
    protected void before() {
        greenMail.start();
    }

    @Override
    protected void after() {
        greenMail.stop();
    }

    @Override
    public void beforeEach(ExtensionContext context) {
        before();
    }

    @Override
    public void afterEach(ExtensionContext context) {
        after();
    }

    public int getMessagesCount() {
        return greenMail.getReceivedMessages().length;
    }

    public List<MimeMessage> getMessages() {
        return Arrays.asList(greenMail.getReceivedMessages());
    }

    public MimeMessage getFirstMessage() {
        MimeMessage message = null;
        MimeMessage[] receivedMessages = greenMail.getReceivedMessages();
        if (receivedMessages.length > 0) {
            message = receivedMessages[0];
        }
        return message;
    }

}
  • package を ksbysample.common.test.rule.mailksbysample.common.test.extension.mail に変更します(ファイルも移動します)。
  • クラス名を MailServerResourceMailServerExtension に変更します。
  • implements BeforeEachCallback, AfterEachCallback を追加します。
  • beforeEach メソッドを override し、中で before メソッドを呼び出します。
  • afterEach メソッドを override し、中で after メソッドを呼び出します。

最初に JUnit 4 のテストで正常に動作することを確認します。src/test/java/ksbysample/webapp/lending/helper/mail/EmailHelperTest.javaJUnit 4 のテストとして以下のように実装されていますが、

package ksbysample.webapp.lending.helper.mail;

import ksbysample.common.test.extension.mail.MailServerExtension;
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.context.SpringBootTest;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.test.context.junit4.SpringRunner;

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

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

@RunWith(SpringRunner.class)
@SpringBootTest
public class EmailHelperTest {

    @Rule
    @Autowired
    public MailServerExtension mailServer;

    @Autowired
    private JavaMailSender mailSender;

    @Autowired
    private EmailHelper emailHelper;

    @Test
    public void testSendSimpleMail() throws Exception {
        SimpleMailMessage message = new SimpleMailMessage();
        message.setFrom("test@sample.com");
        message.setTo("xxx@yyy.zzz");
        message.setSubject("テスト");
        message.setText("これはテストです");
        emailHelper.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("これはテストです");
        emailHelper.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("これはテストです"));
    }

}

これを実行するとテストは成功します。

f:id:ksby:20190309091042p:plain

次に JUnit 5 で以下のように書き直してから、

package ksbysample.webapp.lending.helper.mail;

import ksbysample.common.test.extension.mail.MailServerExtension;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;

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

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertAll;

@SpringBootTest
public class EmailHelperTest {

    @RegisterExtension
    @Autowired
    public MailServerExtension mailServer;

    @Autowired
    private JavaMailSender mailSender;

    @Autowired
    private EmailHelper emailHelper;

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

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

    @Test
    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("これはテストです");
        emailHelper.sendMail(message.getMimeMessage());

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

}
  • import org.junit.Test;import org.junit.jupiter.api.Test; に変更します。
  • @RunWith(SpringRunner.class) を削除します。
  • @Rule@RegisterExtension に変更します。
  • テストメソッドの public を削除します。
  • MimeMessage receiveMessage = mailServer.getFirstMessage(); を呼び出した後のメールの内容を確認している部分を assertAll を使用するよう変更します。また assertThat は JUnit 4 の org.junit.Assert.assertThat だったので AssertJ の assertThat に変更します。

実行すると、こちらもテストが成功します。

f:id:ksby:20190309092106p:plain

SecurityMockMvcResource クラスを JUnit 5 の Extension としても使用できるようにする

src/test/java/ksbysample/common/test/rule/mockmvc/SecurityMockMvcResource.java は以下の実装ですが、

package ksbysample.common.test.rule.mockmvc;

import org.junit.rules.ExternalResource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Component;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;

@Component
@ConditionalOnWebApplication
public class SecurityMockMvcResource extends ExternalResource {

    public final String MAILADDR_TANAKA_TARO = "tanaka.taro@sample.com";
    public final String MAILADDR_SUZUKI_HANAKO = "suzuki.hanako@test.co.jp";
    public final String MAILADDR_KIMURA_MASAO = "kimura.masao@test.co.jp";
    public final String MAILADDR_ENDO_YOKO = "endo.yoko@sample.com";
    public final String MAILADDR_SATO_MASAHIKO = "sato.masahiko@sample.com";
    public final String MAILADDR_TAKAHASI_NAOKO = "takahasi.naoko@test.co.jp";
    public final String MAILADDR_ITO_AOI = "ito.aoi@test.co.jp";

    @Autowired
    private WebApplicationContext context;

    @Autowired
    private UserDetailsService userDetailsService;

    public MockMvc authTanakaTaro;
    public MockMvc authSuzukiHanako;
    public MockMvc authItoAoi;
    public MockMvc noauth;

    @Override
    protected void before() throws Throwable {
        // 認証ユーザ用MockMvc ( user = tanaka.taro@sample.com )
        UserDetails userDetailsTanakaTaro = userDetailsService.loadUserByUsername(MAILADDR_TANAKA_TARO);
        this.authTanakaTaro = MockMvcBuilders.webAppContextSetup(this.context)
                .defaultRequest(get("/").with(user(userDetailsTanakaTaro)))
                .apply(springSecurity())
                .build();

        // 認証ユーザ用MockMvc ( user = suzuki.hanako@test.co.jp )
        UserDetails userDetailsSuzukiHanako = userDetailsService.loadUserByUsername(MAILADDR_SUZUKI_HANAKO);
        this.authSuzukiHanako = MockMvcBuilders.webAppContextSetup(this.context)
                .defaultRequest(get("/").with(user(userDetailsSuzukiHanako)))
                .apply(springSecurity())
                .build();

        // 認証ユーザ用MockMvc ( user = ito.aoi@test.co.jp )
        UserDetails userDetailsItoAoi = userDetailsService.loadUserByUsername(MAILADDR_ITO_AOI);
        this.authItoAoi = MockMvcBuilders.webAppContextSetup(this.context)
                .defaultRequest(get("/").with(user(userDetailsItoAoi)))
                .apply(springSecurity())
                .build();

        // 非認証ユーザ用MockMvc
        this.noauth = MockMvcBuilders.webAppContextSetup(this.context)
                .apply(springSecurity())
                .build();
    }

}

これを以下のように変更します。

package ksbysample.common.test.extension.mockmvc;

import org.junit.jupiter.api.extension.BeforeEachCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.rules.ExternalResource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Component;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;

@Component
@ConditionalOnWebApplication
public class SecurityMockMvcExtension extends ExternalResource
        implements BeforeEachCallback {

    public final String MAILADDR_TANAKA_TARO = "tanaka.taro@sample.com";
    public final String MAILADDR_SUZUKI_HANAKO = "suzuki.hanako@test.co.jp";
    public final String MAILADDR_KIMURA_MASAO = "kimura.masao@test.co.jp";
    public final String MAILADDR_ENDO_YOKO = "endo.yoko@sample.com";
    public final String MAILADDR_SATO_MASAHIKO = "sato.masahiko@sample.com";
    public final String MAILADDR_TAKAHASI_NAOKO = "takahasi.naoko@test.co.jp";
    public final String MAILADDR_ITO_AOI = "ito.aoi@test.co.jp";

    @Autowired
    private WebApplicationContext context;

    @Autowired
    private UserDetailsService userDetailsService;

    public MockMvc authTanakaTaro;
    public MockMvc authSuzukiHanako;
    public MockMvc authItoAoi;
    public MockMvc noauth;

    @Override
    protected void before() {
        // 認証ユーザ用MockMvc ( user = tanaka.taro@sample.com )
        UserDetails userDetailsTanakaTaro = userDetailsService.loadUserByUsername(MAILADDR_TANAKA_TARO);
        this.authTanakaTaro = MockMvcBuilders.webAppContextSetup(this.context)
                .defaultRequest(get("/").with(user(userDetailsTanakaTaro)))
                .apply(springSecurity())
                .build();

        // 認証ユーザ用MockMvc ( user = suzuki.hanako@test.co.jp )
        UserDetails userDetailsSuzukiHanako = userDetailsService.loadUserByUsername(MAILADDR_SUZUKI_HANAKO);
        this.authSuzukiHanako = MockMvcBuilders.webAppContextSetup(this.context)
                .defaultRequest(get("/").with(user(userDetailsSuzukiHanako)))
                .apply(springSecurity())
                .build();

        // 認証ユーザ用MockMvc ( user = ito.aoi@test.co.jp )
        UserDetails userDetailsItoAoi = userDetailsService.loadUserByUsername(MAILADDR_ITO_AOI);
        this.authItoAoi = MockMvcBuilders.webAppContextSetup(this.context)
                .defaultRequest(get("/").with(user(userDetailsItoAoi)))
                .apply(springSecurity())
                .build();

        // 非認証ユーザ用MockMvc
        this.noauth = MockMvcBuilders.webAppContextSetup(this.context)
                .apply(springSecurity())
                .build();
    }

    @Override
    public void beforeEach(ExtensionContext context) {
        before();
    }

}
  • package を ksbysample.common.test.rule.mockmvcksbysample.common.test.extension.mockmvc に変更します。
  • クラス名を SecurityMockMvcResourceSecurityMockMvcExtension に変更します。
  • implements BeforeEachCallback を追加します。
  • before メソッドの throws Throwable を削除します。
  • beforeEach メソッドを override し、中で before メソッドを呼び出します。

現在作成済のテストで SecurityMockMvcExtension を使用しているものは TestDataResource も一緒に使用しているので、TestDataResource を変更してから動作確認します。

次回へ続く。。。

長くなったので次回へ続きます。

履歴

2019/03/09
初版発行。