久しぶりにテストコードを書いたのでMEMO〜。
アプリから他のサービスを使っているけど、自動テストの時はそのサービスを使いたくない。
もしくは、テスト対象のクラスから他のクラスのメソッドを呼び出しているが、他のクラスの仕様はそのクラスのテストに委譲しており、今回のテストには含めたくない。
ただ、作成したクラスからサービスや他のクラスに対して、どんな値が渡されたのかはチェックしたい。
例えば、Controller
クラスのテストをする場合、 Controller
内で Fukuzatsu
クラスの hikisuIppai()
メソッドを呼び出しているとする。
でも、hikisuIppai()
メソッドの挙動は Fukuzatsu
クラス側でテストするから、hikisuIppai()
メソッドの「呼び出し回数」と「引数として渡された値」が想定通りに設定されていることだけをテストできればOKとする。
そうした時のテストクラスはこんな感じになる。
import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.SpyBean; import org.springframework.test.web.servlet.MockMvc; import java.math.BigDecimal; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.*; import static org.hamcrest.core.Is.is; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; @AutoConfigureMockMvc @SpringBootTest class ControllerTest { @Autowired private MockMvc mockMvc; @SpyBean private Fukuzatsu fukuzatsu; @Captor private ArgumentCaptor<Hotel> hotel; @Captor private ArgumentCaptor<Reservation> reservation; @Test void detailTest(@Autowired ObjectMapper mapper) throws Exception { doReturn(null).when(this.fukuzatsu).hikisuIppai(any(), any(), any(), any()); CreateCommand cmd = new CreateCommand(hoge, fuga, piyo); String json = mapper.writeValueAsString(cmd); mockMvc.perform(post("/detail").content(json)); verify(this.fukuzatsu, times(1)).hikisuIppai(any(), hotel.capture(), reservation.capture(), any()); assertThat(reservation.getValue().getRating(), allOf(greaterThanOrEqualTo(new BigDecimal(0)), lessThanOrEqualTo(new BigDecimal(5)))); assertThat(reservation.getValue().getNumberOfPeople(), is(greaterThanOrEqualTo(0))); } }
内部の処理をスキップさせるために@SpyBean
を利用して Fukuzatsu
クラスの hikisuIppai
メソッドの処理をモック化している。
今回は、引数に何が来ても null
を返すように。
@SpyBean private Fukuzatsu fukuzatsu; ... doReturn(null).when(this.fukuzatsu).hikisuIppai(any(), any(), any(), any());
アサーションでは、 hikisuIppai
メソッドの呼び出し回数と、引数として渡した値を検査している。
キャプチャできれば普通に is
メソッドなどを使って比較ができる。
今回は、
hikisuIppai
メソッドの呼び出し回数が1回であることhikisuIppai
メソッドの2番目、3番目に渡した値が想定通りであること- 2番目の値は
hotel
プロパティにキャプチャして、hotel.getValue()
で値を取得している。 - 3番目の値は
reservation
プロパティにキャプチャして、reservation.getValue()
で値を取得している。
- 2番目の値は
を検査している。
@Captor private ArgumentCaptor<Hotel> hotel; @Captor private ArgumentCaptor<Reservation> reservation; ... verify(this.fukuzatsu, times(1)).hikisuIppai(any(), hotel.capture(), reservation.capture(), any()); assertThat(hotel.getValue().getRating(), allOf(greaterThanOrEqualTo(new BigDecimal(0)), lessThanOrEqualTo(new BigDecimal(5)))); assertThat(reservation.getValue().getNumberOfPeople(), is(greaterThanOrEqualTo(0)));