2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

SpringのControllerクラスのテストコード

Posted at

はじめに

SpringのControllerクラスのテストをJUnitで行う。

テスト対象クラス

登録された商品の一覧の取得機能(displayList())、商品IDで検索し商品を取得する機能(displayDetails)を実装した。
Service層はモック化するため紹介を省くが、リポジトリ層を呼び出しDBにアクセスする。
DBから取得した値をmodelに格納してからviewに返却しthymeleafを使って表示する。

CatalogController.java
@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を利用する。

CatalogControllerTest.class
@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のテストコードも書いて学習したい。

参考

Controllerクラスのテスト
プロになるためのSpring入門ーーゼロからの開発力養成講座

2
0
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
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?