摘要:ASP.NET MVP UnitTest
相較於ASP MVC 的UnitTest,談論ASP WebForm 的UnitTest 就比較少了
而MVP(Model View Presenter) 的架構提供給 ASP WebForm 一個比較容易貼近UI UnitTest 的方式
借用一個簡單的範例,計算BMI值的範例來說明 (BMI = 體重 / 身高(m)平方
建立一個計算BMI的Service
public class HealthService { 2
3
public double GetBMIValue(double height, double weight) { 4
return Math.Round(weight/ Math.Pow(height / 100, 2), 0); 5
} 6
}
HealthService 的UnitTest(只簡單驗證一項數值)
[TestMethod] 02
public void HealthService_GetBMIValue_Test() { 03
//arrange 04
var service = new HealthService(); 05
06
//act 07
var result = service.GetBMIValue(170, 60); 08
09
//assert 10
Assert.AreEqual(21, result); 11
}
再來看ASP.net WebForm,採用MVP 的模式
建立一個View : IHealthView
public interface IHealthView { 02
03
//身高 04

double Height { get; } 05
06
//體重 07
double Weight { get; } 08
09
//BMI值 10
double BMIValue { set; } 11
}
以及一個Presenter : HealthPresenter
public class HealthPresenter { 02
03
private IHealthView view; 04
05
public HealthPresenter(IHealthView view) { 06
this.view = view; 07
} 08
09
//計算BMI 10
public void CalcBMI() { 11
var service = new HealthService(); 12
var bmiValue = service.GetBMIValue(view.Height, view.Weight); 13
14
view.BMIValue = bmiValue; 15
} 16
}建立Health.aspx 頁面,實作 IHealthView interface
<form id="form1" runat="server"> 2
Height : 3
<asp:TextBox ID="txtHeight" runat="server"></asp:TextBox><br /> 4
Weight : 5
<asp:TextBox ID="txtWeight" runat="server"></asp:TextBox><br /> 6
<asp:Button ID="btnCalc" runat="server" Text="Calc" onclick="btnCalc_Click" /><br /> 7
BMI : <asp:Label ID="lblBMI" runat="server"></asp:Label> 8
</form>Health.aspx.cs
public partial class Health : System.Web.UI.Page, IHealthView { 02
03
private HealthPresenter presenter; 04
05
protected void Page_Load(object sender, EventArgs e) { 06
presenter = new HealthPresenter(this); 07
} 08
09
#region IHealthView Members 10
11
public double Height { 12
get { return double.Parse(txtHeight.Text); } 13
} 14
15
public double Weight { 16
get { return double.Parse(txtWeight.Text); } 17
} 18
19
public double BMIValue { 20
set { this.lblBMI.Text = value.ToString(); } 21
} 22
23
#endregion 24
25
protected void btnCalc_Click(object sender, EventArgs e) { 26
presenter.CalcBMI(); 27
} 28
}接下看看UnitTest部分,此Code有用的Moq 這個Mock component
[TestMethod] 02
public void HealthPresenter_Test() { 03
//arrange 04
var mockView = new Moq.Mock<IHealthView>(); 05
mockView.Setup(m => m.Height).Returns(170); 06
mockView.Setup(m => m.Weight).Returns(60); 07
var presenter = new HealthPresenter(mockView.Object); 08
09
//act 10
presenter.CalcBMI(); 11
12
//assert 13
mockView.VerifySet(m => m.BMIValue = 21); 14
}Presenter 只與 View 互動,而不管實作的UI是什麼(Aspx, WinForm, ....),View只單存做資料的提供與接收
但上面的例子有個值得探討之處,就是當UI按下Cacl 的Button時,View 去呼叫Presenter的Method (CalcMBI),這樣子會讓View 多負擔個職責,他必須知道Presenter 的Method且控制Presenter
這樣子 View本身就不是單單的資料提供與接收,且這樣的作法, Presenter 會失去應該份演的角色,反而變成一個service 的proxy
因此修改一下IHealthView,以event-driven, 增加個event
//發生計算BMI事件 2
event EventHandler CalcBMI;
修改Health.aspx的btnCalc_Click ,當按下Calc Button時,觸發 CalcBMI event
//..... 2
public event EventHandler CalcBMI; 3
4
protected void btnCalc_Click(object sender, EventArgs e) { 5
if (CalcBMI != null) { 6
CalcBMI(this, EventArgs.Empty); 7
} 8
}HealthPresenter也要修改
public class HealthPresenter { 02
03
private IHealthView view; 04
05
public HealthPresenter(IHealthView view) { 06
this.view = view; 07
view.CalcBMI += new EventHandler(view_CalcBMI); 08
} 09
10
void view_CalcBMI(object sender, EventArgs e) { 11
var service = new HealthService(); 12
var bmiValue = service.GetBMIValue(view.Height, view.Weight); 13
14
view.BMIValue = bmiValue; 15
} 16
}
這樣子,View 本身就不再控制Presenter method,而是單純地觸發event,而Presenter 則是依據View 所發生的event,去執行所要做到事情 (計算BMI,並將結果傳給IHealthView)
再來修改UnitTest 的作法,一樣利用Moq 模擬event
[TestMethod] 02
public void HealthPresenter_Test() { 03
//arrange 04
var mockView = new Moq.Mock<IHealthView>(); 05
mockView.Setup(m => m.Height).Returns(170); 06
mockView.Setup(m => m.Weight).Returns(60); 07
var presenter = new HealthPresenter(mockView.Object); 08
09
//act 10
mockView.Raise(v => v.CalcBMI += null, EventArgs.Empty); 11
12
//assert 13
mockView.VerifySet(m => m.BMIValue = 21); 14
}這裡簡單介紹關於MVP 用於ASP.net WebForm 的UnitTest
至於MSDN 有一篇關於MVP的文章,可以參考 Beyond MVP
public double GetBMIValue(double height, double weight)
public void CalcBMI()