2016-08-15 70 views
6

在過去,當我一直在實現單元測試時,我一直在努力爲數據訪問層設置'體面的'單元測試,因爲它們通常有一個數據庫作爲外部依賴。在理想的世界中,我會模擬存儲過程調用,以便刪除外部依賴項。c#單元測試,嘲諷存儲過程

然而,我一直無法解決如何用MOQ嘲笑框架來做到這一點,或者找到任何其他支持這個框架的框架。相反,我已經恢復創建一個帶有已知數據的腳本測試數據庫(這樣我總能得到我期望的輸出),但這與Mocking該層稍有不同。

任何人都可以建議如何模擬這部分數據訪問層[我知道的實體框架,https://effort.codeplex.com/存在]?


詳細 舉例來說,如果我有以下方法

public object RunStoredProc() 
{ 
    //Some Setup 

    using (SqlConnection conn = new SqlConnection(CONNNECTION_STRING)) 
    { 
     using (SqlCommand comm = new SqlCommand("storedProcName", conn)) 
     { 
      conn.Open(); 
      comm.CommandType = CommandType.StoredProcedure; 
      using (SqlDataReader reader = comm.ExecuteReader()) 
      { 
       while (reader.Read()) 
       { 
        //Logic 
       } 
      } 
     } 
    } 

    //Return object based on logic 
} 

然後我怎麼嘲笑存儲過程的輸出,使SQLDataReader包含指定的數據。在更高層次上,我可以嘲笑RunStoredProc()方法 - 但這不會幫助我測試該方法中的邏輯是否正確。我也可以剝離SQLReader伸到另一種方法

public object RunStoredProc() 
{ 
    //Some Setup 

    List<object> data = GetData(); 
    //Logic 

    //Return object based on logic 
} 

private List<object> GetData() 
{ 
    using (SqlConnection conn = new SqlConnection(CONNNECTION_STRING)) 
    { 
     using (SqlCommand comm = new SqlCommand("storedProcName", conn)) 
     { 
      conn.Open(); 
      comm.CommandType = CommandType.StoredProcedure; 
      using (SqlDataReader reader = comm.ExecuteReader()) 
      { 
       while (reader.Read()) 
       { 
        //place into return object 
       } 
      } 
     } 
    } 
} 

,但作爲「的GetData」方法應該是私有的(未公佈的接口的一部分),然後我就無法嘲笑這一點,所以問題仍然。

回答

3

我認爲我們擁有所有的接口(IDbConnectionIDbTransactionIDbCommandIDataReader),並借用EF(IDbConnectionFactory)的想法,需要,使他們可以嘲笑與依賴注入使用抽象的一切。我認爲SqlConnection和其他更多的是實現細節,可以抽象。

繼的想法從實體框架,你可以創建一個連接工廠

public interface IDbConnectionFactory { 
    /// <summary> 
    /// Creates a connection based on the given database name or connection string. 
    IDbConnection CreateConnection(string nameOrConnectionString); 
} 

然後你就可以重構只能用抽象的實例方法。

public class MyDataAccessClass { 
    private IDbConnectionFactory dbConnectionFactory; 
    private string CONNNECTION_STRING = "Connection string here"; 

    public MyDataAccessClass(IDbConnectionFactory dbConnectionFactory) { 
     this.dbConnectionFactory = dbConnectionFactory; 
    } 

    public object RunStoredProc() { 
     //Some Setup 
     List<object> result = new List<object>(); 

     using (IDbConnection conn = dbConnectionFactory.CreateConnection(CONNNECTION_STRING)) { 
      using (IDbCommand comm = conn.CreateCommand()) { 
       comm.CommandText = "storedProcName"; 
       conn.Open(); 
       comm.CommandType = CommandType.StoredProcedure; 
       using (IDataReader reader = comm.ExecuteReader()) { 
        while (reader.Read()) { 
         //...Logic to populate result 
        } 
       } 
      } 
     } 

     //Return object based on logic 
     return result; 
    } 
} 

從那裏,你嘲笑使用你選擇的模擬框架接口或創建自己的假貨注入和測試方法。

[TestClass] 
public class StoredProcedureUnitTest { 
    [TestMethod] 
    public void TestRunStoredProc() { 
     //Arrange 
     var connectionFactory = new Mock<IDbConnectionFactory>(); 
     //..Setup... 

     var sut = new MyDataAccessClass(connectionFactory.Object); 

     //Act 
     var actual = sut.RunStoredProc(); 

     //Assert 
     //... 
    } 
} 
+0

感謝,看起來像它會工作,如果我讀這正確我需要模擬將IDbCommand.ExecuteReader()的唯一方法[我還需要模擬IDbConnection.CreatCommand到返回嘲笑的IDbCommand但這應該是微不足道的]。如果其他人有任何其他想法,我將暫時擱置一段時間。 –