はじめに
SpringのControllerクラスのテストをJUnitで行う。
テスト対象クラス
登録された商品の一覧の取得機能(displayList())、商品IDで検索し商品を取得する機能(displayDetails)を実装した。
Service層はモック化するため紹介を省くが、リポジトリ層を呼び出しDBにアクセスする。
DBから取得した値をmodelに格納してからviewに返却しthymeleafを使って表示する。
@Controller
@RequestMapping("/catalog")
public class CatalogController {
private final CatalogService catalogService;
public CatalogController(CatalogService catalogService) {
this.catalogService = catalogService;
}
@GetMapping("/display-list")
public String displayList(Model model) {
List<Product> products = catalogService.findAll();
model.addAttribute("productList", products);
return "catalog/productList";
}
@GetMapping("/display-details")
public String displayDetails(@RequestParam String productId, Model model) {
Product product = catalogService.findById(productId);
model.addAttribute("product", product);
return "catalog/productDetails";
}
}
Controlleクラスのテスト
つづいてCatalogController.javaをテストする。
単にメソッドをテストするだけであれば、テストクラスから直接メソッドを呼び出せばテストを行えるが、これでは@GetMapping()などのアノテーションが機能しているかのテストができない。
アノテーションも含めてテストするために、MockMvcを利用する。
@WebMvcTest(CatalogController.class)
class CatalogControllerTest {
@Autowired
MockMvc mockMvc;
@MockBean
CatalogService catalogService;
@Test
void displayList_test() throws Exception {
List<Product> products = new ArrayList<Product>();
Product product = new Product();
product.setName("商品01");
products.add(product);
product = new Product();
product.setName("商品02");
products.add(product);
doReturn(products).when(catalogService).findAll();
mockMvc.perform(
get("/catalog/display-list")
)
.andDo(print())
.andExpect(content().string(containsString("商品01")))
.andExpect(content().string(containsString("商品02")))
.andExpect(view().name("catalog/productList"));
}
@Test
void displayDetails_test() throws Exception {
Product product = new Product();
product.setName("商品01");
doReturn(product).when(catalogService).findById("p01");
mockMvc.perform(
get("/catalog/display-details")
.param("productId", "p01")
)
.andDo(print())
.andExpect(content().string(containsString("商品01")))
.andExpect(view().name("catalog/productDetails"));
}
}
@WebMvcTestでテスト対象クラスを指定する。
MockMvcをインジェクションし、@MockBeanにテスト対象クラスのインスタンスをモック化する。
doReturn()にモック化するオブジェクトの戻り値を指定する。
when()にモック化したオブジェクトを指定し、それに続いてメソッドを呼び出す。
doReturn(products).when(catalogService).findAll();
テスト対象のメソッドの指定と、モック化したオブジェクト(catalogService)のメソッドの引数を指定する。
get()にはControllerクラスに記載したエントリポイントを指定する。
param()にはモック化したオブジェクトのメソッドの引数を指定する。
mockMvc.perform(
get("/catalog/display-details")
.param("productId", "p01")
)
続いてテストの期待値の指定を行う。
content()には戻り値(ここではviewに返すHTML)を指定する。戻り値に「商品01」が含まれているかを確認する。
view()には返却するview名を確認する。
.andDo(print())
.andExpect(content().string(containsString("商品01")))
.andExpect(content().string(containsString("商品02")))
.andExpect(view().name("catalog/productList"));
.andDo(print())でコンソールに戻り値を表示し、視覚的に確認することができる。
テストケースが膨大な場合は、コンソール出力で処理が重くなるため注意。
コンソール出力を一部抜粋する。
MockHttpServletRequest:
HTTP Method = GET
Request URI = /catalog/display-list
Parameters = {}
Headers = []
Body = null
Session Attrs = {}
Handler:
Type = com.example.shopping.controller.CatalogController
Method = com.example.shopping.controller.CatalogController#displayList(Model)
Async:
Async started = false
Async result = null
Resolved Exception:
Type = null
ModelAndView:
View name = catalog/productList
View = null
Attribute = productList
value = [com.example.shopping.entity.Product@34549979, com.example.shopping.entity.Product@144a5e6e]
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 200
Error message = null
Headers = [Content-Language:"en", Content-Type:"text/html;charset=UTF-8"]
Content type = text/html;charset=UTF-8
Body = <!DOCTYPE HTML>
<html>
<head>
<title>商品一覧</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<h1>商品一覧</h1>
<table>
<tr>
<th>商品名</th>
<th>価格</th>
<th>在庫数</th>
</tr>
<tr>
<td><a href="/catalog/display-details?productId="><span>商品01</span></a></td>
<td><span></span>円</td>
<td><span></span></td>
</tr>
<tr>
<td><a href="/catalog/display-details?productId="><span>商品02</span></a></td>
<td><span></span>円</td>
<td><span></span></td>
</tr>
</table>
</body>
</html>
Forwarded URL = null
Redirected URL = null
Cookies = []
おわりに
@RestControllerを指定した、所謂REST APIのテストコードも書いて学習したい。