使用Moq对数据表导出服务做单元测试

Use Moq to do unit testing for datatable export service

我有一个导出为 csv 文件的服务,它在 WPF 应用程序的视频模型中:

 public void ExportToExcel()
    {

        DataTable tblFiltered = _currentTable.AsEnumerable()
                          .Where(r => r.Field<string>("Type") == ExportGridGroup)
                          .CopyToDataTable();

        StringBuilder sb = new StringBuilder();
        IEnumerable<string> columnNames = tblFiltered.Columns.Cast<DataColumn>().
                                          Select(column => column.ColumnName);
        sb.AppendLine(string.Join(",", columnNames));

        foreach (DataRow row in tblFiltered.Rows)
        {
            IEnumerable<string> fields = row.ItemArray.Select(field => "\"" + field.ToString() + "\"");
            sb.AppendLine(string.Join(",", fields));
        }

        String result = sb.ToString();
        try
        {
            StreamWriter sw = new StreamWriter("export.csv");
            sw.WriteLine(result);
            sw.Close();
            Process.Start("export.csv");
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
        }
    }

所以基于此,我为它创建了一个测试方法,但还没有完成;

 [TestMethod()]
    public void ExportToExcelTest()
    {
        //Step 1: Creating a mock table with columns
        DataTable testtable = new DataTable();
        DataRow mydatarow;
        mydatarow = testtable.NewRow();
        //Step 2: Adding the row 
        mydatarow["ColumnA"] = "12345";
        mydatarow["ColumnB"] = "test";

        testtable.Rows.Add(mydatarow);

        StringBuilder sb = new StringBuilder();
        IEnumerable<string> columnNames = testtable.Columns.Cast<DataColumn>().
                                          Select(column => column.ColumnName);
        sb.AppendLine(string.Join(",", columnNames));

        foreach (DataRow row in testtable.Rows)
        {
            IEnumerable<string> fields = row.ItemArray.Select(field => "\"" + field.ToString() + "\"");
            sb.AppendLine(string.Join(",", fields));
        }

        String result = sb.ToString();
        StreamWriter sw = new StreamWriter("export.csv");
        sw.WriteLine(result);
        sw.Close();
        Process.Start("export.csv");          
        Assert.Fail();
    }

我有点糊涂了,因为在测试方法中,我写的代码几乎和项目中一模一样..... 我可以就如何编写此单元测试和有关 WPF/C# Unit Testing by using Moq for me start with 的任何详细参考提供一些建议吗?

考虑将 try 块中的代码提取到另一个服务

public interface ICsvWriter
{
    void WriteOutput(string text);
}

并将其作为依赖项传递给导出服务的构造函数

public Service(ICsvWriter csvWriter)
{
    this.csvWriter = csvWriter;
}

然后,您可以编写这样的测试来验证它是否确实输出了您期望的结果。

[TestMethod]
public void TestMethod1()
{
    var csvWriter= new Mock<ICsvWriter>();
    var service = new Service(csvWriter.Object);
    string exportedText = string.Empty;

    // set up your mock so that whatatever string you pass in
    // gets passed to your exportedText variable.
    csvWriter.Setup(s => s.WriteOutput(It.IsAny<string>())).Callback<string>(s => exportedText = s);

    var testtable = new DataTable();
    // .... add test data

    // set the table on the view model
    service.SetData(testtable);

    // call the export method.
    service.ExportToExcel();

    // check that the result matches your expectation
    Assert.AreEqual("the text you expect to be written", exportedText);
}

更新

您可以通过使用参数化测试以相同的测试方法测试不同的场景来进一步改进您的测试。

如果您使用的是 MSTest,则有一个名为 DataRow 的属性。对于 xUnit,它称为 InlineData,对于 NUnit,它称为 TestCase。他们都是这样工作的:

[TestMethod]
[DataRow("12345", "test", "expected output text")]
[DataRow("abcde", "123", "expected output text")]
[DataRow("fghij", "456", "expected output text")]
public void TestMethod1(string columnA, string columnB, string expectedOutput)
{
    // ....
}

更新

您还可以通过传入 DataTable 作为参数来提高导出方法的可测试性。然后你可以在没有 public 属性 设置数据的情况下测试它。

public void ExportToExcel(DataTable currentTable)
{
    DataTable tblFiltered = currentTable.AsEnumerable()
                          .Where(r => r.Field<string>("Type") == ExportGridGroup)
                          .CopyToDataTable();

    // ... 
}

另一个更新

实际上,使用回调是不必要的。抱歉,我应该建议这样做更干净:

这样设置:

csvWriter.Setup(s => s.WriteOutput(It.IsAny<string>()));

然后这样验证:

csvWriter.Verify(s => s.WriteOutput("expected text"), Times.Once);