LoginSignup
2
4

More than 3 years have passed since last update.

Spring BootでモックしたいBeanがテスト対象Beanで宣言されてない場合のMock化をする

Posted at

やりたいこと

呼び出しが、以下のような感じ
Controller -> Service -> Repository -> Component
ControllerからとかServiceからテスト書く時に@Mock@InjectMocksではComponentのBeanをモック化できなかったので@MockBeanを使用することに

環境

  • 諸事情あり、JUnit4を使ってます
build.gradle
plugins {
    id 'org.springframework.boot' version '2.2.5.RELEASE'
    id 'io.spring.dependency-management' version '1.0.9.RELEASE'
    id 'java'
}

group = 'com.example.mock'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'

    testImplementation('org.springframework.boot:spring-boot-starter-test')
}

test {
    useJUnitPlatform()
}

🍥MockBean

ソース

SampleContoller.java
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api/")
public class SampleController {

    SampleService sampleService;

    public SampleController(SampleService sampleService) {
        this.sampleService = sampleService;
    }

    public String controllerから文字列返す() {
        return sampleService.serviceから文字列返す();
    }
}
SampleService.java
import org.springframework.stereotype.Service;

@Service
public class SampleService {

    SampleRepository sampleRepository;

    public SampleService(SampleRepository sampleRepository) {
        this.sampleRepository = sampleRepository;
    }

    String serviceから文字列返す() {
        return sampleRepository.repositoryから文字列返す();
    }
}
SampleRepository.java
import org.springframework.stereotype.Repository;

@Repository
public class SampleRepository {

    SampleApi sampleApi;

    public SampleRepository(SampleApi sampleApi) {
        this.sampleApi = sampleApi;
    }

    String repositoryから文字列返す() {
        return sampleApi.sampleApiから文字列返す();
    }
}
SampleApi.java
import org.springframework.stereotype.Component;

/**
 * 外部サービスへ呼び出しのクラス
 */
@Component
public class SampleApi {

    public String sampleApiから文字列返す() {
        return "駆け出しエンジニア";
    }
}
SampleControllerTest.java
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.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit4.SpringRunner;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;

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

    @MockBean
    SampleApi sampleApi;

    @Autowired
    SampleController sut;

    @Test
    public void mockBean使ってテスト() {
        when(sampleApi.sampleApiから文字列返す()).thenReturn("抜け出しエンジニア");
        assertThat(sut.controllerから文字列返す()).isEqualTo("駆け出しエンジニア");
    }
}

結果

モックされてる

org.opentest4j.AssertionFailedError: 
Expecting:
 <"抜け出しエンジニア">
to be equal to:
 <"駆け出しエンジニア">
but was not.
Expected :駆け出しエンジニア
Actual   :抜け出しエンジニア

🍥Mockでも試してみる

使うクラスは上と一緒です
@Mockつけて、テスト対象クラスに@InjectMock

SampleController -> SampleApi

ソース

SampleControllerTest.java
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;
import static org.mockito.MockitoAnnotations.initMocks;

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

    @Mock
    SampleApi sampleApi;

    @InjectMocks
    @Autowired
    SampleController sut;

    @Test
    public void mockBean使ってテスト() {
        initMocks(this);
        when(sampleApi.sampleApiから文字列返す()).thenReturn("抜け出しエンジニア");
        assertThat(sut.controllerから文字列返す()).isEqualTo("駆け出しエンジニア");
    }
}

結果

モックされてる気がしない

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.2.5.RELEASE)

2020-03-09 22:27:32.270  INFO 3106 --- [           main] c.e.mock.mockdemo.SampleControllerTest   : Starting SampleControllerTest on kihisakawaryoutsuginoMacBook-Pro.local with PID 3106 (started by ri2kku in /Users/xxx/sandbox/java/mock-demo)
2020-03-09 22:27:32.271  INFO 3106 --- [           main] c.e.mock.mockdemo.SampleControllerTest   : No active profile set, falling back to default profiles: default
2020-03-09 22:27:33.164  INFO 3106 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2020-03-09 22:27:33.352  INFO 3106 --- [           main] c.e.mock.mockdemo.SampleControllerTest   : Started SampleControllerTest in 1.305 seconds (JVM running for 1.965)
2020-03-09 22:27:33.798  INFO 3106 --- [extShutdownHook] o.s.s.concurrent.ThreadPoolTaskExecutor  : Shutting down ExecutorService 'applicationTaskExecutor'

Process finished with exit code 0

SampleController -> SampleService

ソース

SampleControllerTest.java
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;
import static org.mockito.MockitoAnnotations.initMocks;

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

    @Mock
    SampleService sampleService;

    @InjectMocks
    @Autowired
    SampleController sut;

    @Test
    public void mockBean使ってテスト() {
        initMocks(this);
        when(sampleService.serviceから文字列返す()).thenReturn("抜け出しエンジニア");
        assertThat(sut.controllerから文字列返す()).isEqualTo("駆け出しエンジニア");
    }
}

結果

モックされてる

org.opentest4j.AssertionFailedError: 
Expecting:
 <"抜け出しエンジニア">
to be equal to:
 <"駆け出しエンジニア">
but was not.
Expected :駆け出しエンジニア
Actual   :抜け出しエンジニア

まとめ

モックしたいBeanがテスト対象Beanで宣言されてない場合は、MockBean使うのがいいのかね。
そもそもモックしたくない。

2
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
4