在TDD(MOQ)中使用Mocks的最佳实践

时间:2012-12-07 08:21:29

标签: c# .net-3.5 tdd nunit moq

我是使用Mocks的新手。但它的主要目的是什么?我将首先使用Moq来测试我的应用程序(和NUnit)。

例如,我的代码可以执行以下操作:

我的网页代码背后:

public partial class MyWebpage
{
    protected string GetTitle(string myVar)
    {
        return dataLayer.GetTitle(myVar);
    }
}

我的数据访问层:

public class DataLayer
{
    public string GetTitle(string myVar)
    {
        // Create the query we want
        string query = "SELECT title FROM MyTable " + 
            "WHERE var = @myVar";

        //ENTER PARAMETERS IN HERE

        // Now return the result to the view
        return this.dataProvider.ExecuteMySelectQuery(
            dr =>
            {
               //DELEGATE DATA READER PASSED IN AND TITLE GETS RETURNED
            },
            query,
            parameters);
    }
}

我的数据提供者直接与数据库进行对话并进行交互:

 public class DataProvider
{
     public T ExecuteMySelectQuery<T>(Func<IDataReader, T> getMyResult, string selectQuery, Dictionary parameters)
    {
          //RUNS AND RETURNS THE QUERY
     }
}

测试所有这些的最佳方法是什么?

2 个答案:

答案 0 :(得分:3)

如果要单独测试图层,则需要为DataProvider和DataLayer类创建接口,以显示您想要模拟的方法。然后你可以使用一个模拟框架 - NSubstitute非常好,用于创建模拟的代码更少 - 模拟对依赖类的调用,让你测试该特定单元内的代码

public interface IDataProvider
{
     T ExecuteMySelectQuery<T>(Func<IDataReader, T> getMyResult, string selectQuery, Dictionary parameters);
}

public interface IDataLayer
{
    string GetTitle(string myVar);
}

public class DataLayer 
{
    private IDataProvider dataProvider;

    public DataLayer(IDataProvider dataProvider)
    {
        this.dataProvider = dataProvider;
    }
}

然后,在测试代码中,创建模拟而不是真实对象,并在实例化测试对象时将它们传递给构造函数。测试DataLayer:

[Test]
public void WhenRetievingTitleFromDataStore_ThenDataLayerReturnsTitle() 
{
    var title = "Title";
    var dataProviderMock = new Mock<IDataProvider>(MockBehavior.Strict);
    dataProviderMock.Setup(x => x.ExecuteMySelectQuery(<parameters>)).Returns(title);
    var dataLayer = new DataLayer(dataProviderMock.Object);
    Assert.That(dataLayer.GetTitle(It.IsAny<string>(), Is.EqualTo(title));
}

答案 1 :(得分:2)

唯一可能出错的是DB调用(查询或返回的结果是错误的数据类型)。这不能被嘲笑。您需要进行集成测试而不是单元测试。

通常,您只能模拟能够在代码中测试逻辑。您应该进行测试,以便数据映射器(this.dataProvider.ExecuteMySelectQuery)按照定义工作。但那是相关代码的范围。

<强>更新

所以你得到了以下课程:

public class DataLayer
{
    public string GetTitle(string myVar)
    {
        // Create the query we want
        string query = "SELECT title FROM MyTable " + 
            "WHERE var = @myVar";

        //ENTER PARAMETERS IN HERE

        // Now return the result to the view
        return this.dataProvider.ExecuteMySelectQuery(
            dr =>
            {
               //DELEGATE DATA READER PASSED IN AND TITLE GETS RETURNED
            },
            query,
            parameters);
    }
}

public class DataProvider
{
     public T ExecuteMySelectQuery<T>(Func<IDataReader, T> getMyResult, string selectQuery, Dictionary parameters)
    {
          //RUNS AND RETURNS THE QUERY
     }
}

如果我们检查ExecuteMySelectQuery,我们可以看到DataLayer类依赖于数据库返回的类型,因为DataProvider只是简化了查询的执行。可以说它是ADO.NET之上的插件。

这也意味着你永远不能保证DataLayer在不涉及数据库的情况下返回所承诺的内容。例如,假设数据库中的table有一个名为title的列,但有人设法使用int数据类型。

可能出错的事情是

  • 查询不正确
  • 数据库中的架构不正确(列名称,数据类型错误等)
  • 映射

在模拟的帮助下,这些错误都不能被检测到或测试。

另一方面,如果你在另一个班级使用DataLayer分类,你当然可以嘲笑它。因为DataLayer类本身就是一个完整的抽象。这意味着该类的调用者不必知道它下面的任何东西。因此,嘲弄完全没问题。