ASP.NET MVP UnitTest

  • 3960
  • 0

摘要:ASP.NET MVP UnitTest

相較於ASP MVC 的UnitTest,談論ASP WebForm  的UnitTest  就比較少了

 而MVP(Model View Presenter) 的架構提供給 ASP WebForm 一個比較容易貼近UI UnitTest 的方式

借用一個簡單的範例,計算BMI值的範例來說明 (BMI = 體重 / 身高(m)平方

建立一個計算BMI的Service

1     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(只簡單驗證一項數值)

01   [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

01     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

01     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

1     <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

01     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

01 [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

1 //發生計算BMI事件
2 event EventHandler CalcBMI;

 

修改Health.aspx的btnCalc_Click ,當按下Calc Button時,觸發 CalcBMI event

1   //.....
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也要修改

01     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

01   [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