2016-08-15 71 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但这应该是微不足道的]。如果其他人有任何其他想法,我将暂时搁置一段时间。 –