程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> 測試驅動開發實踐之重構篇

測試驅動開發實踐之重構篇

編輯:關於.NET

前一篇文章 測試驅動開發實踐-入門篇 我們我們講了一些基本的測試驅動開發流程:

1。寫單元測試使他亮紅燈

2。寫代碼使測試變成綠燈

3。重構代碼

接下來我們需要開始重構了,大家有可能會問,為什麼需要重構,什麼時候開始重構。

對與為什麼需要重構,其實就是為了使代碼結構清晰,去除一些重復的代碼,比如我們執行sql語句操作,我們起初這樣寫

Code

1private connStr="server=.;database=TestDB;uid=sa;pwd=123"
2public int Add(string loginName)
3{
4    int count = 0;
5    using (SqlConnection conn = new SqlConnection(connStr))
6    {
7        conn.Open();
8        SqlCommand cmd = new SqlCommand("insert(loginName) value('" + loginName + "')", conn);
9        count = cmd.ExecuteNonQuery();
10        cmd.Dispose();
11        conn.Close();
12    }
13    return count;
14}
15
16public int Delete(string loginName)
17{
18    int count = 0;
19    using (SqlConnection conn = new SqlConnection(connStr))
20    {
21        conn.Open();
22        SqlCommand cmd = new SqlCommand("delete from LoginUsers where loginName='" + loginName + "'", conn);
23        count = cmd.ExecuteNonQuery();
24        cmd.Dispose();
25        conn.Close();
26    }
27    return count;
28}

我們發現這裡除了sql語句不一樣之外,其他都是一樣的,那我們就可以這樣重構

Code

1private int ExecuteSql(string sql)
2{
3    int count = 0;
4    using (SqlConnection conn = new SqlConnection(connStr))
5    {
6        conn.Open();
7        SqlCommand cmd = new SqlCommand(sql, conn);
8        count = cmd.ExecuteNonQuery();
9        cmd.Dispose();
10        conn.Close();
11    }
12    return count;
13}
14public int Add(string loginName)
15{
16    return ExecuteSql("insert(loginName) value('" + loginName + "')");
17}
18public int Delete(string loginName)
19{
20    return ExecuteSql("delete from LoginUsers where loginName='" + loginName + "'");
21}

這樣重構完之後,代碼是不是清晰了很多呢?

那什麼時候又開始重構呢,我們在覺得代碼重復性太大了,層次結構混亂了等等(就是傳說中的有壞味道的代碼)都可以重構,有測試在,還需要怕代碼改錯嗎?

接下來我們就接著前一篇文章的用例按照 設計模式原則進行重構(單一職責原則,接口隔離原則,依賴倒置原則等)

那麼我們回頭看一下這個登陸的方法裡,包含了驗證和數據操作的代碼,根據單一職責原則,我們需要把他們放在不同的類中,就有了如下代碼

1public interface IEmployeeService
2{
3    bool Login(string loginName, string password);
4    bool ValidateLoginName(string loginName);
5}

在上面代碼中我們定義一個服務層接口,又把驗證單獨拿出來做了一個方法

下面是對服務層的實現

Code

1public class EmployeeService : IEmployeeService
2{
3    private IEmployeeDataAccess _empDAO;
4
5    public EmployeeService(IEmployeeDataAccess empDAO)
6    {
7       
8        _empDAO = empDAO;
9    }
10
11    public bool ValidateLoginName(string loginName)
12    {
13        return !string.IsNullOrEmpty(loginName);
14    }
15
16    /**//// <summary>
17    /// 員工登陸
18    /// </summary>
19    /// <param name="loginName">登陸名</param>
20    /// <param name="password">密碼</param>
21    /// <returns></returns>
22    public bool Login(string loginName, string password)
23    {
24        if (ValidateLoginName(loginName))
25        {
26            if (_empDAO.GetCount(loginName, password) > 0)
27            {
28                return true;
29            }
30        }
31       
32        return false;
33    }
34}

上面服務層我們可以看出,我們是調用了數據層接口的實現,而不是直接調用數據層的操作,這樣的使服務層和數據層的關系進行解偶,服務層不是直接依賴於數據層,而是依賴於數據層接口,

這樣有什麼好處呢?

比如我們現在數據層的實現是和sqlserver進行交互的,下次要與xml進行交互了,那怎麼辦呢,那我們只要實現一個與xml數據交互的數據層就可以了,其他代碼也就不用修改了。

其實模式就是為了 變化 而准備的,假如項目真要交付了,什麼事都沒有了,我們還搞什麼模式呢,你覺得呢?

這裡有點跑題了,接下來我們再回到主題,數據接口的定義如下

1public interface IEmployeeDataAccess
2{
3    int GetCount(string loginName, string password);
4}

數據層我這裡就形式一下了

Code

1public class EmployeeAccess : IEmployeeDataAccess
2{
3    IEmployeeDataAccess 成員#region IEmployeeDataAccess 成員
4
5    public int GetCount(string loginName, string password)
6    {
7        throw new NotImplementedException();
8    }
9
10    #endregion
11}

最後我們還需要修改我們的測試用例,使他繼續運行通過,最後的測試用例如下:

Code

1[TestFixture]
2public class EmployeeServiceTest
3{
4    private IEmployeeService empService;
5
6    [TestFixtureSetUp]
7    public void Init()
8    {
9        var da = new Mock<IEmployeeDataAccess>();
10        da.Setup(dd => dd.GetCount(It.Is<string>(s => s == "admin"), It.Is<string>(s => s == "pwd"))).Returns(1);
11        empService = new EmployeeService(da.Object);
12    }
13
14    [Test]
15    public void LoginTest()
16    {
17        Assert.IsTrue(empService.Login("admin", "pwd"), "登陸失敗");
18    }
19
20    [Test]
21    public void ValidateNullLoginName()
22    {
23        Assert.IsTrue(!empService.ValidateLoginName(null), "用戶為null驗證測試失敗");
24    }
25
26    [Test]
27    public void ValidateEmptyLoginName()
28    {
29        Assert.IsTrue(!empService.ValidateLoginName(""), "用戶為Empty驗證測試失敗");
30    }
31}

這裡重構就到這裡了,這裡服務層測試使用到了moq的mock框架可以在 http://code.google.com/p/moq 下到,所以這裡用了mock模擬數據層進行了測試,這個框架對於分層開發測試非常好,

在數據層沒有寫完的時候,我們就可以模擬數據層提供數據,直接對服務層進行測試。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved