如何模拟多次调用的方法C#

时间:2019-09-16 11:21:22

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

我有一个被多次调用的通用方法(项目中大约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;
    }   
}

我模拟了数据库类,以免单元测试中的数据库。 以下是测试用例代码:

[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从模拟方法中返回?还是还有其他更好的方法?

有帮助吗?

1 个答案:

答案 0 :(得分:0)

您需要定义要测试的内容。 少量重构将帮助您更好地测试代码

    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
    }