【问题标题】:How to mock method which is called multiple times c#如何模拟多次调用的方法c#
【发布时间】:2019-09-16 11:21:22
【问题描述】:

我有一个被多次调用的通用方法(项目中大约有 30-35 个引用)。这种方法基本上是从数据库中获取数据到数据表中。

以下是可测试的代码:

public class MyApp
{
   private readonly IDataProvider _dbProvider;
   public MyApp(IDataProvider dbProvider)
   {
       _dbProvider = dbProvider;
   }

   public void Process()
   {
       string query = "something";
       Helper h = new Helper(_dbProvider);
       // This method will be called in the Process method several times
       var data = h.GetData(query);
   }
}

public interface IDataProvider 
{
   IDbConnection CreateConnection(string connectionString);
   DataTable FillDatatableFromAdapter(IDbCommand command);
}

public class DataProvider  : IDataProvider 
{
     public IDbConnection CreateConnection(string connectionString)
     {
         return new SqlConnection(connectionString);
     }

     public DataTable FillDatatableFromAdapter(IDbCommand command)
     {
          DataSet dataSet = new DataSet();
          SqlCommand sqlCommand = command as SqlCommand;
          using (SqlDataAdapter sqlDataAdapter = new SqlDataAdapter(sqlCommand))
          {
              sqlDataAdapter.Fill(dataSet);
          }

          if (dataSet.Tables.Count == 0)
              return null;

          return dataSet.Tables[0];
     }
}

public class Helper
{
   private readonly IDataProvider _dbProvider;
   public Helper(IDataProvider dbProvider)
   {
       _dbProvider = dbProvider;
   }

   public DataTable GetData(string query)
   { 
       DataTable table = new DataTable();
       using (IDbConnection connection = 
       _databaseProvider.CreateConnection(_connectionString))
       {
           connection.Open();
           using (IDbCommand command = connection.CreateCommand())
           {
               command.CommandText = query;
               command.Connection = connection;
               table = _dbProvider.FillDatatableFromAdapter(command);
           }
       }
       return table;
    }   
}

我已经模拟了 DB 类以不从单元测试中命中 DB。 以下是测试用例代码:

[TestMethod]
public void TestMyApp()
{
     Mock<IDbCommand> mockDbCommand = new Mock<IDbCommand>();
     Mock<IDbConnection> mockDbConnection = new Mock<IDbConnection>();
     Mock<IDataProvider> mockDatabaseProvider = new Mock<IDataProvider();

     mockDbConnection.Setup(m => m.CreateCommand()).Returns(mockDbCommand.Object);
     mockDatabaseProvider.Setup(m => m.CreateConnection(It.IsAny<string>())).Returns(mockDbConnection.Object);

     DataTable table = new DataTable();
     mockDatabaseProvider.SetupSequence(mock => mock.FillDatatableFromAdapter(It.IsAny<IDbCommand>()))
                                .Returns(dataTable);

     MyApp app = new MyApp(mockDatabaseProvider.Object);
     app.Process();
   //  And then after that I am testing some data.
}

我将多次调用 GetData() 方法,因此 FillDatatableFromAdapter 也将被多次调用。在上面的测试用例中,我模拟了 FillDatatableFromAdapter 方法,并返回了一些假数据表以供进一步测试。 我知道 Moq 中的 SetupSequence 方法,每次调用该方法时,我都可以使用该方法从模拟方法返回多个数据表。

我想要一个建议是该方法正确,因为那样我将不得不创建许多数据表,这些数据表将从使用 SetupSequence 的模拟方法返回?还是有其他更好的方法?

有什么帮助吗??

【问题讨论】:

  • 它按原样工作吗?
  • 是的,它正在工作,但是我正在创建多个要返回的数据表。
  • 你的Helper 类不应该也实现一个接口吗?因为这是您的 MyApp 类的主要依赖项(Helper 包装了 IDataProvider)。然后你应该在你的测试中模拟 Helper 类,因为你现在想测试 MyApp 类的逻辑

标签: c# .net unit-testing mocking moq


【解决方案1】:

您需要定义要测试的内容。 一个小的重构将帮助你更好地测试你的代码

    public class MyApp
    {
       private readonly IDataProvider _dbProvider;
       private readonly IHelper _h;
       public MyApp(IDataProvider dbProvider)
       {
           _dbProvider = dbProvider;
           _h = new Helper(_dbProvider);
       }

       public void Process()
       {
           string query = "something";
           // This method will be called in the Process method several times
           var data = _h.GetData(query);
       }
    }

    public interface IDataProvider 
    {
       IDbConnection CreateConnection(string connectionString);
       DataTable FillDatatableFromAdapter(IDbCommand command);
    }

    public class DataProvider  : IDataProvider 
    {
         public IDbConnection CreateConnection(string connectionString)
         {
             return new SqlConnection(connectionString);
         }

         public DataTable FillDatatableFromAdapter(IDbCommand command)
         {
              DataSet dataSet = new DataSet();
              SqlCommand sqlCommand = command as SqlCommand;
              using (SqlDataAdapter sqlDataAdapter = new SqlDataAdapter(sqlCommand))
              {
                  sqlDataAdapter.Fill(dataSet);
              }

              if (dataSet.Tables.Count == 0)
                  return null;

              return dataSet.Tables[0];
         }
    }

public interface IHelper
{
     public DataTable GetData(string query);
}

public class Helper
{
   private readonly IDataProvider _dbProvider;
   public Helper(IDataProvider dbProvider)
   {
       _dbProvider = dbProvider;
   }

   public DataTable GetData(string query)
   { 
       DataTable table = new DataTable();
       using (IDbConnection connection = 
       _databaseProvider.CreateConnection(_connectionString))
       {
           connection.Open();
           using (IDbCommand command = connection.CreateCommand())
           {
               command.CommandText = query;
               command.Connection = connection;
               table = _dbProvider.FillDatatableFromAdapter(command);
           }
       }
       return table;
    }   
}

现在您可以进行 4 个独立的测试

    [TestMethod]
    public void TestProccessFunctionCallHelperGetData()
    {
         Mock<IHelper> mockHelper = new Mock<IHelper>();
         Mock<IDataProvider> mockDatabaseProvider = new Mock<IDataProvider();

         mockHelper.Setup(m => m.getData(It.IsAny<string>())).Returns(new DataTable());
         MyApp app = new MyApp(mockDatabaseProvider.Object, mockHelper.Setup);
         app.Process();
         Assert.AreEquals(mockHelper.numTimesCalled, 1);
    }

    [TestMethod]
    public void TestHelperGetData()
    {
        var query = ""; //TODO: write dummy query
        var dataTableToReturn = new DataTable(); //TODO: add some data
        Mock<IDataProvider> mockDatabaseProvider = new Mock<IDataProvider();
        mockDatabaseProvider.Setup(m => m.CreateConnection(It.IsAny<string>())).Returns(new SqlConnection("dummy connection string"));
        mockDatabaseProvider.Setup(m => m.FillDatatableFromAdapter(It.IsAny<IDbCommand>())).Returns(dataTableToReturn );

        IHelper h = new Helper(mockDatabaseProvider.Object);
        var actualDataTable = h.getData(query);
        Assert.AreEqual(dataTableToReturn, actualDataTable  );

    }

    [TestMethod]
    public void TestDataProviderFillDataTableFromAdapter()
    {
       //This test seems to me more like integration test because you need to mock a db or use a real db 
    }

    [TestMethod]
    public void TestYourData()
    {
       // Create a dataTable with data so you can continue with your testing
    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-02-17
    • 1970-01-01
    • 2022-11-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多