1ヶ月ぶりに更新します。今年はもっとインプットを増やしてアウトプットも増やしていこうと思います!
まずテスト対象クラスはこちらのShopService.java
@Service public class ShopService { @Autowired private ShopRepository shopRepository; public Shop getShop(Integer id) { return shopRepository.findOne(id); } public Shop create(ShopCreateRequest request) { LocalDateTime now = LocalDateTime.now(); Shop shop = new Shop(); shop.setGenre(request.getGenre()); shop.setShopName(request.getShopName()); shop.setStation(request.getStation()); shop.setUrl(request.getUrl()); shop.setMemo(request.getMemo()); shop.setCreatedAt(now); return shopRepository.saveAndFlush(shop); } public Shop edit(Integer id, ShopEditRequest request) { Shop shop = getShop(id); shop.setGenre(request.getGenre()); shop.setShopName(request.getShopName()); shop.setStation(request.getStation()); shop.setUrl(request.getUrl()); shop.setMemo(request.getMemo()); return shopRepository.saveAndFlush(shop); } }
続いてテストクラスは、ShopService.java
import com.uchitate.core.entity.Shop; import com.uchitate.core.repository.ShopRepository; import com.uchitate.web.support.Genre; import com.uchitate.web.support.ShopCreateRequest; import com.uchitate.web.support.ShopEditRequest; import de.bechte.junit.runners.context.HierarchicalContextRunner; import org.assertj.core.api.Assertions; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.*; @RunWith(HierarchicalContextRunner.class) public class ShopServiceTest { @InjectMocks private ShopService shopService; @Mock private ShopRepository shopRepository; @Captor private ArgumentCaptor<Shop> captor; @Before public void setup() { MockitoAnnotations.initMocks(this); } public class Create { private ShopCreateRequest generateShopCreateRequest( Genre genre, String shopName, String station, String url, String memo) { ShopCreateRequest request = new ShopCreateRequest(genre, shopName, station, url, memo); return request; } @Test public void success() { ShopCreateRequest request = generateShopCreateRequest( Genre.JAPANESE, "定食屋1", "渋谷", "http://teisyoku-1.co.jp", "トンカツが有名"); shopService.create(request); Mockito.verify(shopRepository).saveAndFlush(captor.capture()); Shop savedShop = captor.getValue(); Assertions.assertThat(savedShop) .extracting(Shop::getGenre, Shop::getShopName, Shop::getStation, Shop::getUrl, Shop::getMemo) .contains(Genre.JAPANESE, "定食屋1", "渋谷", "http://teisyoku-1.co.jp", "トンカツが有名"); } } public class Edit { private ShopEditRequest generateShopEditRequest( Genre genre, String shopName, String station, String url, String memo) { ShopEditRequest request = new ShopEditRequest(genre, shopName, station, url, memo); return request; } private Shop generateShop(int id, Genre genre, String shopName, String station, String url, String memo) { Shop shop = new Shop(); shop.setId(id); shop.setGenre(genre); shop.setShopName(shopName); shop.setStation(station); shop.setUrl(url); shop.setMemo(memo); return shop; } @Test public void success() { ShopEditRequest request = generateShopEditRequest( Genre.JAPANESE, "定食屋1", "渋谷", "http://teisyoku-1.co.jp", "トンカツが有名"); Shop shop = generateShop(1, Genre.ITALIAN, "イタリアン1", "代官山", "http://italian-1.co.jp", "ピザ"); Mockito.when(shopRepository.findOne(Mockito.anyInt())).thenReturn(shop); shopService.edit(1, request); Mockito.verify(shopRepository).saveAndFlush(captor.capture()); Shop savedShop = captor.getValue(); Assertions.assertThat(savedShop) .extracting(Shop::getGenre, Shop::getShopName, Shop::getStation, Shop::getUrl, Shop::getMemo) .contains(Genre.JAPANESE, "定食屋1", "渋谷", "http://teisyoku-1.co.jp", "トンカツが有名"); } } }
では、解説。
@RunWith(HierarchicalContextRunner.class)
ランナーは、「HierarchicalContextRunner.class」を指定します。
github.com
このクラスは、テストクラスの階層構造を可能にします。ただそれだけといえばそれだけです。
見た目が美しくなります。私は、テスト対象クラスのメソッド単位でテストクラスを作ってます。
まず、pom.xmlに以下を追加します。
<dependency> <groupId>de.bechte.junit</groupId> <artifactId>junit-hierarchicalcontextrunner</artifactId> <version>4.12.1</version> </dependency>
あとは、
@RunWith(HierarchicalContextRunner.class)
と書くだけです。
@InjectMocks
このクラスにモックを注入しますよアノテーション
@Mock
モックにするよアノテーション
@Captor
好きなところの状態のオブジェクトを取得したいよアノテーション
@Captor private ArgumentCaptor<Shop> captor;
Shop.javaのオブジェクトを取得することを指定します。
次をテスト対象クラスのメソッドを呼んだあとに書きます。
Mockito.verify(shopRepository).saveAndFlush(captor.capture()); Shop savedShop = captor.getValue();
これで、「shopRepository.saveAndFlush(shop);」を呼ぶ時に渡す引数のオブジェクトを取得します。
editは、ほぼ同じなので省略します。
以上、ユニットテストのナレッジ #1でした〜