狠狠撸
Submit Search
Spring bootでweb ユニットテスト编
?
18 likes
?
13,752 views
なべ
Follow
厂辫谤颈苍驳プロジェクトでのユニットテストの仕方
Read less
Read more
1 of 51
Download now
Downloaded 41 times
More Related Content
Spring bootでweb ユニットテスト编
1.
ユニットテスト编
2.
アジェンダ ? はじめに ? ユニットテストの必要性 ?
ユニットテストの適用分野 ? ユニットテストをするためのプロダクト ? Springでのユニットテスト ? Resopitoryのユニットテスト ? Serviceのユニットテスト ? Controllerのユニットテスト(Validate) ? Controllerのユニットテスト(画面遷移) ? まとめ
3.
はじめに ? ここでは、ユニットテストの必要性と、 ユニットテストの書き方を説明する 単体テスト ユニットテスト 結合テスト 総合テスト?システムテスト ユーザーテスト?運用テスト 一般的なテストの流れ ここの話
4.
ユニットテストの必要性 ? こんな経験ありませんか?
5.
ユニットテストの必要性 ? こんな経験ありませんか? A:「あれ?この処理がエラーで停止するよ?」
6.
ユニットテストの必要性 ? こんな経験ありませんか? A:「あれ?この処理がエラーで停止するよ?」 B:「そんなはずはないですよ。テストしましたから」(1ヶ月前に)
7.
ユニットテストの必要性 ? こんな経験ありませんか? A:「あれ?この処理がエラーで停止するよ?」 B:「そんなはずはないですよ。テストしましたから」(1ヶ月前に) C:「あ、共通処理直しました」 (先週)
8.
ユニットテストの必要性 ? こんな経験ありませんか? A:「あれ?この処理がエラーで停止するよ?」 B:「そんなはずはないですよ。テストしましたから」(1ヶ月前に) C:「あ、共通処理直しました」 (先週) A:「そのときこの処理のテストしたの?」
9.
ユニットテストの必要性 ? こんな経験ありませんか? A:「あれ?この処理がエラーで停止するよ?」 B:「そんなはずはないですよ。テストしましたから」(1ヶ月前に) C:「あ、共通処理直しました」 (先週) A:「そのときこの処理のテストしたの?」 C:「してないですね。1行しか直してないですし」
10.
ユニットテストの必要性 ? こんな経験ありませんか? A:「あれ?この処理がエラーで停止するよ?」 B:「そんなはずはないですよ。テストしましたから」(1ヶ月前に) C:「あ、共通処理直しました」 (先週) A:「そのときこの処理のテストしたの?」 C:「してないですね。1行しか直してないですし」 A:「え?」
叠:「え?」
11.
ユニットテストの必要性 ? こんな経験ありませんか? A:「あれ?この処理がエラーで停止するよ?」 B:「そんなはずはないですよ。テストしましたから」(1ヶ月前に) C:「あ、共通処理直しました」 (先週) A:「そのときこの処理のテストしたの?」 C:「してないですね。1行しか直してないですし」 A:「え?」
叠:「え?」 C:「???え??」
12.
ユニットテストの必要性 ? 問題点1:過去の実績は現在においては不要 B:「そんなはずはないですよ。テストしましたから」(1ヶ月前に) C:「あ、共通処理直しました」 (先週) 開発中のプロジェクトにおいて 過去にパスしたテストが 現在もパスするとは限らない
13.
ユニットテストの必要性 ? 問題点1:過去の実績は現在においては不要 B:「そんなはずはないですよ。テストしましたから」(1ヶ月前に) C:「あ、共通処理直しました」 (先週) 開発中のプロジェクトにおいて 過去にパスしたテストが 現在もパスするとは限らない テストするためには 開発が終わっていないと 着手できない
14.
ユニットテストの必要性 ? 問題点1:過去の実績は現在においては不要 B:「そんなはずはないですよ。テストしましたから」(1ヶ月前に) C:「あ、共通処理直しました」 (先週) 開発中のプロジェクトにおいて 過去にパスしたテストが 現在もパスするとは限らない テストするためには 開発が終わっていないと 着手できない 開発が終わりテストをパスした コードは不具合等がない限り 修正してはならない
15.
ユニットテストの必要性 ? 問題点1:過去の実績は現在においては不要 B:「そんなはずはないですよ。テストしましたから」(1ヶ月前に) C:「あ、共通処理直しました」 (先週) 開発中のプロジェクトにおいて 過去にパスしたテストが 現在もパスするとは限らない テストするためには 開発が終わっていないと 着手できない 開発が終わりテストをパスした コードは不具合等がない限り 修正してはならない 理想的とは思えない汚コードの 修正は許されず、担当者が変 わるとメンテが不可能になる 余談???
16.
ユニットテストの必要性 ? 問題点1:過去の実績は現在においては不要の解決策 B:「そんなはずはないですよ。テストしましたから」(1ヶ月前に) C:「あ、共通処理直しました」 (先週) 開発中のプロジェクトにおいて 過去にパスしたテストが 現在もパスするとは限らない パターンを網羅した テスト用のコードを書く if
( 処理(パターンAのデータ) != パターンAの正常結果 ) { エラー } if ( 処理(パターンBのデータ) != パターンBの正常結果 ) { エラー }
17.
if ( 処理(パターンAのデータ)
!= パターンAの正常結果 ) { エラー } if ( 処理(パターンBのデータ) != パターンBの正常結果 ) { エラー } ユニットテストの必要性 ? 問題点1:過去の実績は現在においては不要の解決策 B:「そんなはずはないですよ。テストしましたから」(1ヶ月前に) C:「あ、共通処理直しました」 (先週) テストするためには 開発が終わっていないと 着手できない テスト用のコードがあれば いつでもテストが出来る
18.
ユニットテストの必要性 ? 問題点1:過去の実績は現在においては不要の解決策 B:「そんなはずはないですよ。テストしましたから」(1ヶ月前に) C:「あ、共通処理直しました」 (先週) 開発が終わりテストをパスした コードは不具合等がない限り 修正してはならない テスト用のコードがあれば コードを直しても結果が 変わらないことを担保できる if
( 処理(パターンAのデータ) != パターンAの正常結果 ) { エラー } if ( 処理(パターンBのデータ) != パターンBの正常結果 ) { エラー }
19.
if ( 処理(パターンAのデータ)
!= パターンAの正常結果 ) { エラー } if ( 処理(パターンBのデータ) != パターンBの正常結果 ) { エラー ユニットテストの必要性 ? 問題点1:過去の実績は現在においては不要の解決策 B:「そんなはずはないですよ。テストしましたから」(1ヶ月前に) C:「あ、共通処理直しました」 (先週) 理想的とは思えない汚コードの 修正は許されず、担当者が変 わるとメンテが不可能になる 余談??? 結果が変わらないことが担保で きているので、常に理想的で きれいなコードを保てる
20.
ユニットテストの必要性 ? 問題点2:修正の影響範囲の判定は職人技 A:「そのときこの処理のテストしたの?」 C:「してないですね。1行しか直してないですし」 修正量に応じて テストの要/不要が 変わることはない
21.
ユニットテストの必要性 ? 問題点2:修正の影響範囲の判定は職人技 A:「そのときこの処理のテストしたの?」 C:「してないですね。1行しか直してないですし」 修正量に応じて テストの要/不要が 変わることはない 修正範囲の影響は呼出階層 等を参照して判断するが、 基本的に技術者のスキルによる
22.
ユニットテストの必要性 ? 問題点2:修正の影響範囲の判定は職人技 A:「そのときこの処理のテストしたの?」 C:「してないですね。1行しか直してないですし」 修正量に応じて テストの要/不要が 変わることはない 修正範囲の影響は呼出階層 等を参照して判断するが、 基本的に技術者のスキルによる 全てのテストをやり直せば 職人技の判断が不要になるが それには時間と人手がかかる
23.
ユニットテストの必要性 ? 問題点2:修正の影響範囲の判定は職人技 A:「そのときこの処理のテストしたの?」 C:「してないですね。1行しか直してないですし」 修正量に応じて テストの要/不要が 変わることはない 修正範囲の影響は呼出階層 等を参照して判断するが、 基本的に技術者のスキルによる 全てのテストをやり直せば 職人技の判断が不要になるが それには時間と人手がかかる 担当者によってテストの精度に バラツキが出て、リスク回避の為 修正を避けるようになる
24.
ユニットテストの必要性 ? 問題点2:修正の影響範囲の判定は職人技の解決策 A:「そのときこの処理のテストしたの?」 C:「してないですね。1行しか直してないですし」 修正量に応じて テストの要/不要が 変わることはない 修正範囲の影響は呼出階層 等を参照して判断するが、 基本的に技術者のスキルによる 全てのテストをやり直せば 職人技の判断が不要になるが それには時間と人手がかかる 担当者によってテストの精度に バラツキが出て、リスク回避の為 修正を避けるようになる テストを書けば 回避できる
25.
ユニットテストの適用分野 ? 適用に向く分野 ?共通処理プログラム ?パターンが多い処理プログラム ?ビジネスロジック ?データ更新/取得処理プログラム ?リリースを頻繁に繰り返すプロダクト などなど??? ? 適用に向かない分野 ?画面の見た目(パーツの配置) ?画面の情報を更新する処理(スプレッドの更新など) ?一度リリースしたら終了するプロダクト あくまで例として???
26.
ユニットテストの適用分野 ? 継続的インテグレーション(CI:continuous integration) 開発者
ソース管理 CIツールステージングサーバー 1.コミット 2.pull 6.結果の通知 3.ビルド 4.ユニットテストの実施 5.デプロイ
27.
ユニットテストをするためのプロダクト ? 以下の様な代表的なプロダクトが存在する プロダクト 説明 JUnit
Java用のユニットテストのパイオニア NUnit JUnitをベースにした .NET用のプロダクト Visual Studio Unit Testing Framework Visual Studio付属のプロダクト DbUnit データベース操作に特化したJava用のプロダクト 以降ではJUnitを使用して、 Springで書いたJavaソースの ユニットテストを実施する
28.
Springでのユニットテスト ? どこでなにをテストするかがポイント どこ なにを Controller(画面制御)
全ての入力項目に対して、全てのパターンの 入力値を与えた妥当性(バリデート)チェック 画面遷移 Service(ビジネスロジック) パターンを網羅したロジックのチェック Repository(データの入出力) データ取得の確認 データ保存の確認 見た目の確認や負荷テスト等は JUnitではテストしない
29.
Resopitoryのユニットテスト ? テスト用のクラスを準備をする /** ProductRepositoryのUT
*/ @RunWith(SpringJUnit4ClassRunner.class) @SpringApplicationConfiguration(classes = App.class) public class ProductRepositoryTest { /** 事前処理 */ @Before public void 事前処理( ) throws Exception { } /** 事後処理 */ @After public void 事後処理( ) throws Exception { } } JUnitでSpringを動かすオマジナイ Springを起動するクラスを記述 テストの事前処理 テストの事後処理
30.
Resopitoryのユニットテスト ? テスト対象のRepositoryを宣言 /** ProductRepositoryのUT
*/ @RunWith(SpringJUnit4ClassRunner.class) @SpringApplicationConfiguration(classes = App.class) public class ProductRepositoryTest { @Autowired private ProductRepository productRepository; テスト対象のRepositoryを コンテナから取得
31.
Resopitoryのユニットテスト ? サンプルデータの準備 /** 事前処理
*/ @Before public void 事前処理() throws Exception { Product product1 = new Product(); product1.productCode = 1; product1.productName = "ガム"; product1.price = 100; productRepository.save(product1); Product product2 = new Product(); product2.productCode = 2; product2.productName = "アメ"; product2.price = 120; productRepository.save(product2); } テスト用データを 事前処理で用意する (これ自体が保存のテストと 言えなくもない)
32.
Resopitoryのユニットテスト ? テストの実行 /** findOneメソッドのテスト
*/ @Test public void findOneメソッドのテスト( ) { Product product = productRepository.findOne(1); assertEquals ( product.productName, "ガム“ ); } テスト用メソッドだと宣言する 事前処理で用意した データを取得する 評価メソッドで結果を評価する product.productNameが 「ガム」ではない場合、 例外が発生しテスト失敗となる ※あくまでサンプルとして既定の処理を呼び出している。 本来は自身で作成した処理を呼んでテストする。
33.
Resopitoryのユニットテスト ? 代表的な評価メソッド メソッド 説明 assertEquals
( A , B ) A と B が同じ値か評価する assertTrue( A ) assertFalse( A ) A が true か false か評価する assertNull( A ) assertNotNull( A ) A が null か nullではない か評価する fail( ) 強制的にエラーとする
34.
Serviceのユニットテスト ? テスト用のクラスを準備をする /** ProductServiceのUT
*/ @RunWith(SpringJUnit4ClassRunner.class) @SpringApplicationConfiguration(classes = App.class) public class ProductServiceTest { /** 事前処理 */ @Before public void 事前処理( ) throws Exception { } /** 事後処理 */ @After public void 事後処理( ) throws Exception { } } JUnitでSpringを動かすオマジナイ Springを起動するクラスを記述 テストの事前処理 テストの事後処理
35.
Serviceのユニットテスト ? テスト対象のServiceを宣言 /** ProductServiceのUT
*/ @RunWith(SpringJUnit4ClassRunner.class) @SpringApplicationConfiguration(classes = App.class) public class ProductServiceTest { @Autowired private ProductService productService ; テスト対象のServiceを コンテナから取得
36.
Serviceのユニットテスト ? 事前準備 /** 事前処理
*/ @Before public void 事前処理() throws Exception { } 何かあれば記述する
37.
Serviceのユニットテスト ? テストの実行 /** プロダクト一覧を取得できるかのテスト
*/ @Test public void プロダクト一覧を取得できるかのテスト() { List<Product> list = productService.getProductList(); } テスト用メソッドだと宣言する テスト対象の ビジネスロジックを呼ぶ 評価メソッドで結果を評価する (このサンプルでは未実装) リストの件数や内容を評価する コードを記述する
38.
Controllerのユニットテスト(Validate) ? テスト用のクラスを準備をする /** ProductFormのUT
*/ @RunWith(SpringJUnit4ClassRunner.class) @SpringApplicationConfiguration(classes = App.class) public class ProductFormTest { /** 事前処理 */ @Before public void 事前処理( ) throws Exception { } /** 事後処理 */ @After public void 事後処理( ) throws Exception { } } JUnitでSpringを動かすオマジナイ Springを起動するクラスを記述 テストの事前処理 テストの事後処理
39.
Controllerのユニットテスト(Validate) ? テスト用のバリデータを宣言 /** ProductFormのUT
*/ @RunWith(SpringJUnit4ClassRunner.class) @SpringApplicationConfiguration(classes = App.class) public class ProductFormTest { /** バリデータ */ private Validator validator; 妥当性検証を実行する バリデータを宣言
40.
Controllerのユニットテスト(Validate) ? 事前準備 /** 事前処理
*/ @Before public void 事前処理() throws Exception { validator = Validation.buildDefaultValidatorFactory( ) .getValidator( ); } バリデータを初期化
41.
Controllerのユニットテスト(Validate) ? テスト対象のフォーム /** 画面の値を保持するForm
*/ public class ProductForm { @NotNull private Integer productCode; @NotNull @Length(max=10) private String productName; @NotNull private Integer price; 入力必須 入力必須 最大文字数は10文字まで 入力必須
42.
Controllerのユニットテスト(Validate) ? テストの実行 /** productNameの妥当性テスト
*/ @Test public void productNameの妥当性テスト( ) throws Exception { ProductForm productForm = new ProductForm( ); productForm.setProductCode( 1 ); productForm.setProductName( "ポテトチップスうすしお“ ); productForm.setPrice( 100 ); // バリデート Set<ConstraintViolation<ProductForm>> violations = validator.validate( productForm ); // 検証 assertEquals( violations.size( ), 1 ); for (ConstraintViolation<ProductForm> v: violations) { assertTrue( v.getConstraintDescriptor( ).getAnnotation( ) instanceof Length); テスト用メソッドだと宣言する テスト対象の フォームを初期化 productNameのみが 11文字でエラーになるはず
43.
Controllerのユニットテスト(Validate) ? テストの実行 ProductForm productForm
= new ProductForm( ); productForm.setProductCode( 1 ); productForm.setProductName( "ポテトチップスうすしお“ ); productForm.setPrice( 100 ); // バリデート Set<ConstraintViolation<ProductForm>> violations = validator.validate( productForm ); // 検証 assertEquals( violations.size( ), 1 ); for (ConstraintViolation<ProductForm> v: violations) { assertTrue( v.getConstraintDescriptor( ).getAnnotation( ) instanceof Length); } } バリデートの実行 エラーの数は 1 つかチェック エラーの種類が Length かチェック
44.
Controllerのユニットテスト(画面遷移) ? テスト用のクラスを準備をする /** ProductControllerのUT
*/ @RunWith(SpringJUnit4ClassRunner.class) @WebAppConfiguration @SpringApplicationConfiguration(classes = App.class) public class ProductControllerTest { /** 事前処理 */ @Before public void 事前処理( ) throws Exception { } /** 事後処理 */ @After public void 事後処理( ) throws Exception { } } JUnitでSpringを動かすオマジナイ (Webアプリ用宣言を追加) Springを起動するクラスを記述 テストの事前処理 テストの事後処理
45.
Controllerのユニットテスト(画面遷移) ? テスト用のコンテキストとMVCのモックを宣言 /** ProductControllerのUT
*/ @RunWith(SpringJUnit4ClassRunner.class) @WebAppConfiguration @SpringApplicationConfiguration(classes = App.class) public class ProductControllerTest { @Autowired private WebApplicationContext wac; private MockMvc mockMvc; アプリケーションの設定等々を 管理するコンテキスト リクエストとレスポンスと、 それに付随する情報の モックオブジェクト
46.
Controllerのユニットテスト(画面遷移) ? 事前準備 /** 事前処理
*/ @Before public void 事前処理() throws Exception { mockMvc = webAppContextSetup(wac).build(); } モックを初期化
47.
Controllerのユニットテスト(画面遷移) ? テストの実行(入力画面の初期表示の) /**入力画面の初期表示のテスト */ @Test public
void 入力画面の初期表示のテスト( ) throws Exception { mockMvc.perform(get("/product/input")) .andExpect(status().isOk()) .andExpect(model().hasNoErrors()); } テスト用メソッドだと宣言する URL「/public/input」に GETメソッドでアクセスする 結果のHTTPステータスが OK(200)か評価する Model(フォームの内容)に エラー情報がないか評価する
48.
Controllerのユニットテスト(画面遷移) ? テストの実行(入力画面から確認画面に遷移) /** 入力画面から確認画面に遷移するテスト
*/ @Test public void 入力画面から確認画面に遷移するテスト() throws Exception { ResultActions resultActions = mockMvc.perform(post("/product/confirm") .contentType(MediaType.APPLICATION_FORM .param("productCode", "1") .param("productName", "ガム") .param("price", "100") ); // レスポンスの検証 resultActions.andExpect(status().isOk()) .andExpect(model().hasNoErrors()); テスト用メソッドだと宣言する URL「/public/confirm」に POSTメソッドでアクセスする 入力画面で入力した値として 各種パラメータを付加する
49.
Controllerのユニットテスト(画面遷移) ? テストの実行(入力画面から確認画面に遷移) // レスポンスの検証 resultActions.andExpect(status().isOk()) .andExpect(model().hasNoErrors()); //
モデルの内容の検証 ModelMap modelMap = resultActions.andReturn( ) .getModelAndView( ).getModelMap( ); ProductForm productForm = (ProductForm)modelMap .get("productForm"); assertEquals(productForm.getProductCode(), new Integer(1)); assertEquals(productForm.getProductName(), "ガム"); assertEquals(productForm.getPrice(), new Integer(100)); } モデルからフォームを取得 リクエスト時に付加したパラメータが フォームから取得できるか評価する 結果のHTTPステータスが OK(200)か評価する Model(フォームの内容)に エラー情報がないか評価する
50.
まとめ ? ユニットテストで単体テストを補完する → 画面を開いて境界値テスト等するより向いている ?
ユニットテストを過信しない → ユニットテストをすれば単体テストをしなくて良いわけではない ? ユニットテストでテストのコストは下がらない → コードを書くので画面を開いてテストするよりコストはかかる → 時にはテストコードのテストも必要 ? テストをするのが目的で、テストを書くのが目的ではない → コーディングが楽しいからと言って、目的を失ってはいけない → テスト中毒は程々に ? 本体のコードを修正したらテストコードを直すのはマスト → テストが通らないテストコードはメンテナンスする意識を一気に失う → そのためテストコードのメンテナンスは怠らない(ビルドエラーは論外)
51.
まとめ ? 参考 ■Spring MVC
3.2のSpring MVC Testを触った - コンピュータクワガタ http://kuwalab.hatenablog.jp/entry/20130402/p1 ■ Spring3(というかJSR-303)でBeanValidationのテスト ? ひよっこ。 https://prepro.wordpress.com/2011/01/15/spring3%E3%81%A8%E 3%81%84%E3%81%86%E3%81%8Bjsr- 303%E3%81%A7beanvalidation%E3%81%AE%E3%83%86%E3%82 %B9%E3%83%88/ ■ JSR 303 Bean Validationで遊んでみるよ! - Yamkazu's Blog http://yamkazu.hatenablog.com/entry/20110206/1296985545 ■私のBeanValidationの使い方(Java EE Advent Calendar 2013) — 裏紙 http://backpaper0.github.io/2013/12/03/javaee_advent_calendar_2 013.html
Download