Upload
normalian
View
9.250
Download
2
Tags:
Embed Size (px)
DESCRIPTION
CLR/H 69回目勉強会でお話しさせて頂いた、ASP.NET MVC での TDD 基礎見直しです。あじゅらーもASP.NET MVC な人も是非ご一読あれ
Citation preview
基礎から見直す ASP.NET MVC の単体テスト自動化方法
~ Windows Azure 関連もあるかも~
Microsft MVP for Windows Azure
割と普通
1
自己紹介
• 割と普通 ( @normalian )
– Windows Azure のコミュニティメンバ
• Japan Windows Azure User Group http://r.jazug.jp/
– わんくま同盟 のコミュニティメンバ
• http://www.wankuma.com/
– Microsoft MVP for Windows Azure 2010~
2
本セッションの目的とゴール
• 目的
– C#/VB.NET で単体テスト自動化するため、どのようにテストコードを記述すれば良いかを認知
– 単体テスト自動化を支援するツール、ライブラリ群を認知
• ゴール
– C#/VB.NET で、単体テストの自動化が可能なテストコードを効率的に記述することができる
3
アジェンダ
何故 単体テスト自動化 が必要?
単体テスト自動化 のコツ
単体テストを効率化するツール群
まとめ
参考
何で単体テスト自動化が必要?
• 良く言われるのは以下のメリット
– 単体テストの工数を削減できる
– コードの保守・再利用性が向上する
– コードが綺麗になる
5
単体テストの工数を削減できる
6
仕様変更が増える程、単体テスト工数は増大
コードの保守・再利用性が向上する
• 変更に伴うデグレを瞬時に検知できる
– リファクタリングや保守が容易
– 単体テストコードの動作検証がいつでも可能
• 外部仕様が容易に理解できる
– メソッドの外部仕様がテストコードに記載
– ドキュメント・コードの二重化を防止
7
コードが綺麗になる
• ソースコードのテスタビリティが向上する
– 単体テストの自動化を意識したインターフェースの定義を強制
– テストコードをマニュアルとして利用可能
– コーディング力が向上
• 若手、新人の教育向けに適用できる
8
アジェンダ
何故 単体テスト自動化 が必要?
単体テスト自動化 のコツ
単体テストを効率化するツール群
まとめ
参考
単体テストの対象を明確化する
• データの入出力ポイントに対して単体テストを実施する
コントローラ
画面
モデル
Java
Script
HTML/
CSS モデル
JavaScript で記述したロジックのテスト
JavaScript から C# へのマッピング
モデルのテスト
10 C#
JavaScript
コントローラのテスト
単体テスト自動化が容易なインターフェースを定義する 1/3
• 従来の ASP.NET Web Forms では、単体テストの自動化がきわめて困難
メインコード
テストコード
単体テスト自動化が容易なインターフェースを定義する 2/3
• ASP.NET MVC で単体テストの自動化が容易に
メインコード
テストコード
単体テスト自動化が容易なインターフェースを定義する 3/3
• Testing Framework や NUnit 等で自動テスト可能な外部インターフェース設計とする
13
コントローラ • ViewBag
• Model
ViewReult
Web API の応答
XXXXResult (その他の応答)
変数名の命名規則に留意する 1/2
• テストメソッド名、変数名からチェック対象を理解できるように命名する
14
[TestMethod]
public void Indexがリストを返す()
{
List<string> expect = new List<string>(){ "a", "b", "c" };
List<string> actual = null;
HomeController controller = new HomeController();
actual = (controller.Index() as ViewResult).Model;
Assert.Equals( expect, actual );
}
良い例
変数名の命名規則に留意する 2/2
• 「悪い例」では、テストメソッド名、変数名からチェック対象が理解できない
15
[TestMethod]
public void Index ()
{
HomeController controller = new HomeController();
var actual = (controller.Index() as ViewResult).Model;
Assert.Equals(new List<string>(){ "a", "b", "c" }, actual );
}
悪い例
1メソッド・1アサートを心がける 1/2
• テスト対象を明確化するため、1メソッド・1アサートを心がける
• if 文, for 文, while 文に Assert はダメ、絶対
16
string expect = XXXXXX;
string actual = null;
(中間ロジック)
Assert.Eqauls( expect, actual );
良い例
1メソッド・1アサートを心がける 2/2
• 「悪い例」では、チェックする場所が散って何をチェックしているか分からない
17
string expect = XXXXXX;
string actual = null;
(中間ロジック)
if(flag == true) {
Assert.Eqauls( expect, actual );
}else{
Assert.Fail(“ここは通らないはず”);
}
悪い例
モックを効率的に作成する 1/2
• Moq.dll 等を利用して HttpContextBase, IPrincipal, IIdentity 等のモック作成が難しいクラスを作成する
18
[TestMethod()]
public void IndexTest01()
{
string expect = typeof(RedirectToRouteResult).FullName;
string actual;
BuyHistoryController target = new BuyHistoryController(new TestOrderRepository());
//モックの作成
var mockHttpContextBase = new Mock<HttpContextBase>();
var mockIdentity = new Mock<IIdentity>();
var mockPrincipal = new Mock<IPrincipal>();
//ユーザ情報の設定&httpContextオブジェクトの作成
mockIdentity.Setup(identity => identity.IsAuthenticated).Returns(true);
mockIdentity.Setup(identity => identity.Name).Returns("someUser");
mockPrincipal.Setup(principal => principal.Identity).Returns(mockIdentity.Object);
mockHttpContextBase.Setup(httpContextBase => httpContextBase.User)
.Returns(mockPrincipal.Object);
ControllerContext context = new ControllerContext(mockHttpContextBase.Object, new RouteData(), target);
target.ControllerContext = context;
actual = target.Index().GetType().FullName;
Assert.AreEqual(expect, actual);
}
• Moq.dll を利用してもステップ数が多い
• Moq.dll を利用しないと数倍のコード行数になる
モックを効率的に作成する 2/2
• モック作成は行数が伸びるので共通化する
19
[TestMethod()]
public void IndexTest01(){
string expected= typeof(RedirectToRouteResult).FullName;
string actual;
BuyHistoryController target =
new BuyHistoryController(new TestOrderRepository());
ControllerContext context = new ControllerContext(
Utils.CreateControllerContext(true, "someuser")
, new RouteData(), target);
target.ControllerContext = context;
actual = target.Index().GetType().FullName;
Assert.AreEqual(expect, actual);
}
モック作成を共通化
名前空間、クラス名の命名規則に留意する
• テスト対象のプロジェクト、クラスが分かりやすいように命名規則を規定する
– プロジェクト名:MyMVC → MyMVC.Test
– クラス名: MyClass → MyClassTest
– メソッド名: MyMethod → MyMethodTest
• 命名規約に従うことで、 TestDriven.NET を利用した、メインコード/テストコードの切り替えが可能
– http://www.testdriven.net/quickstart.aspx
20
アジェンダ
何故 単体テスト自動化 が必要?
単体テスト自動化 のコツ
単体テストを効率化するツール群
まとめ
参考
ツールを利用したテスト効率化したい
• 紹介したツール・ライブラリを利用してテストを自動化する
コントローラ
画面
モデル
Java
Script
HTML/
CSS モデル
22 C#
JavaScript
ツールを利用したテスト効率化(β)
• 紹介したツール・ライブラリを利用してテストを自動化する
コントローラ
View
Model
View
Model
HTML/
CSS モデル
C#
JavaScript
knocko
ut.map
ping.js
knock
out.js
AutoM
apper
Entity
Frame
work
23
ツールを利用したテスト効率化(β)
• 紹介したツール・ライブラリを利用してテストを自動化する
コントローラ
View
Model
View
Model
HTML/
CSS モデル
knocko
ut.map
ping.js
knock
out.js
AutoM
apper
Entity
Frame
work
QUnit-tap で単体テスト自動化
node.exe
Testing Framework
単体テスト自動化 MSTest.exe
24 C#
JavaScript
紹介するツール・ライブラリ群
• 今回は以下のライブラリ・ツールを紹介
– knockout.js
– knockout.mapping.js
– AutoMapper
– Qunit-tap
knockout.js
• DOM 要素と JSON オブジェクトのマッピング機能
26
var viewModel = {
left: ko.observable( 30 ),
right: ko.observable( 40 )
}; viewModel.answer = ko.dependentObservable(function () { return parseInt(this.left()) + parseInt(this.right()); }, viewModel);
ko.applyBindings(viewModel);
<input type=“text” data-bind=“value: left” /> + <input type=“text” data-bind=“value: right” /> = <span data-bind="text: answer"></span>
onblur 等のイベントが発生したタイミングで、ViewModel と
DOM要素で値を同期
<script type="text/javascript"> //JSONオブジェクトから、ViewModel を作成 var initialData = @Html.Raw(Json.Encode(Model)); var viewModel = ko.mapping.fromJS(initialData); //DOM要素を読み込み後、ViewModel を Binding $( function(){ ko.applyBindings(viewModel); }); </script>
• C#/VB.NET の ViewModel と JSON オブジェクト をマッピング
27
{ name: “若人”, age: 20}
等のJSONに置換される処理
DOM 要素 と viewModel インスタンスとを双方向バインド
Knockout.mapping.js 1/2
28
$.ajax({ type:“POST”, dataType: "json", contentType: "application/json", data : ko.mapping.toJSON(viewModel), url: "@Url.Content("~/api/Values/")", success : function(res){ alert(JSON.stringify(res)); } });
public class ValuesController : ApiController{ public string Post(ViewModel viewModel){
return "server recieved answer = " + viewModel.answer; }
Knockout.mapping.js 2/2
• C#/VB.NET の ViewModel と JSON オブジェクト をマッピング
AutoMapper
• Model – ViewModel といったモデル間のマッピングを実施する
• 複数モデルからの集約化等、細かな制御が可能
ViewModel Model
項目1
項目2
AutoMapper
• 「XXX.ID = YYY.ID」の羅列を防止できる
• マッピングが存在しない場合は一括でチェック可能
Product product = ProductRepository.GetById(id); Mapping.AutoMapperBootstrapper.Configure(); Mapper.CreateMap<Product, ProductViewModel>(); Mapper.Map<Product, ProductViewModel>(product); var productViewModel = Mapper.Map<Product, ProductViewModel>(product);
QUnit-tap の利用
• node.exe から実行可能な QUnit
– node.exe → Node.js の Windows 実装
– QUnit → ブラウザ上で JavaScript を単体テスト
• knockout.js の ViewModel が対象?
require('../test_helper.js'); QUnit.test('my calc test', function() { var expect = 3; var actual = calc( 1, 2 ); assert.equal(expect, actual); }); QUnit.start();
アジェンダ
何故 単体テスト自動化 が必要?
単体テスト自動化 のコツ
単体テストを効率化するツール群
まとめ
参考
まとめ
• ASP.NET MVC は単体テストの自動化に向いている
• 単体テストの自動化を実施するためにはコツがある
• 単体テストの自動化を支援するツールは多々存在する
View と コントローラの構成 (β)
ViewM
odel View
Model
HTML/CSS Contr
oller
ViewM
odel View
Model
HTML/CSS
ViewM
odel View
Model
HTML/CSS
Model
Model
knockout.map
ping.js knockout.js
C#
JavaScript
1対1 対応 1対N
対応
N対M
対応
対応関係
Entity
Framework
knockout.js
knockout.js
knockout.map
ping.js
knockout.map
ping.js
Entity
Framework
アジェンダ
何故 単体テスト自動化 が必要?
単体テスト自動化 のコツ
単体テストを効率化するツール群
まとめ
参考
参考 1/2
• InfoQ – ASP.NET MVC のテスト方法
– http://www.infoq.com/jp/news/2012/03/aspnet-unit-test
• MSDN Library – ASP.NET MVC アプリケーションの単体テスト
– http://msdn.microsoft.com/ja-jp/library/ff936235.aspx
• ASP.NET MVC3 における単体テストの基礎
– http://codezine.jp/article/detail/6493
• wa りと na はてな日記 - Moq.dll on ASP.NET MVC その2
– http://d.hatena.ne.jp/waritohutsu/20090909/
参考 2/2
• knockout.js Documentation > mapping
– http://knockoutjs.com/documentation/plugins-mapping.html
• knockout.js Documentation > The “template” binding
– http://knockoutjs.com/documentation/template-binding.html
• knockout.js の注意すべき点
– http://d.hatena.ne.jp/shiba-yan/20120130/
• miso_soup3 - AutoMapper+ViewModel In MVC その2
– http://d.hatena.ne.jp/miso_soup3/20120408/
余談!
というか
続編!
クラウド上で単体テスト自動化
• Trac Lightning on Windows Azure – https://github.com/normalian/WATracLightning
テスト自動化もクラウドへ…
単体テスト自動化
(手動キック)
単体テストの自動化
(自動キック)
全てクラウドへ・・・
この
はてしなく遠い
自動テスト坂をよ・・・
オレは
ようやく
のぼりはじめた
ばかりだからな
未完