繁体   English   中英

如何在被测试的类中调用方法?

[英]How do I test a method was called within a class under test?

我首先要说的是我对单元测试很新,我想开始使用TDD方法,但是现在我正在编写一些现有类的单元测试来验证它们在所有情况下的功能。

我已经能够使用NUnit和Rhino模拟测试我的大部分代码而没有太多麻烦。 但是,我一直想知道单元测试函数最终会在同一个类中调用很多其他方法。 我做不了类似的事情

classUnderTest.AssertWasCalled(cut => cut.SomeMethod(someArgs))

因为被测试的课程不是假的。 此外,如果我正在测试的方法调用被测试类中的其他方法,而这些方法又调用同一类中的方法,那么我将需要伪造大量值来测试“顶级”方法。 由于我也是对所有这些“子方法”进行单元测试,我应该能够假设“SomeMethod”如果通过单元测试而无需担心这些低级方法的详细信息,则可以按预期工作。

以下是我一直在使用的一些示例代码,以帮助说明我的观点(我编写了一个类来管理使用NPOI导入/导出Excel文件):

    public DataSet ExportExcelDocToDataSet(bool headerRowProvided)
    {
        DataSet ds = new DataSet();

        for (int i = 0; i < currentWorkbook.NumberOfSheets; i++)
        {               
            ISheet tmpSheet = currentWorkbook.GetSheetAt(i);

            if (tmpSheet.PhysicalNumberOfRows == 0) { continue; }
            DataTable dt = GetDataTableFromExcelSheet(headerRowProvided, ds, tmpSheet);

            if (dt.Rows.Count > 0)
            {
                AddNonEmptyTableToDataSet(ds, dt);
            }
        }

        return ds;
    }

    public DataTable GetDataTableFromExcelSheet(bool headerRowProvided, DataSet ds, ISheet tmpSheet)
    {
        DataTable dt = new DataTable();
        for (int sheetRowIndex = 0; sheetRowIndex <= tmpSheet.LastRowNum; sheetRowIndex++)
        {
            DataRow dataRow = GetDataRowFromExcelRow(dt, tmpSheet, headerRowProvided, sheetRowIndex);
            if (dataRow != null && dataRow.ItemArray.Count<object>(obj => obj != DBNull.Value) > 0)
            {
                dt.Rows.Add(dataRow);
            }
        }

        return dt;
    }

...

您可以看到ExportExcelDocToDataSet (在本例中是我的“顶级”方法)调用GetDataTableFromExcelSheet ,它调用GetDataRowFromExcelRow ,它调用在同一个类中定义的其他几个方法。

那么,为了使其更加单元可测试而不必使用子方法调用的存根值,建议的重构此代码的策略是什么? 有没有办法在被测试的类中伪造方法调用?

在此先感谢您的任何帮助或建议!

修改测试对象(SUT) 如果某些东西难以进行单元测试,那么设计可能会很尴尬。

在被测试的类中进行伪造方法调用会导致超过指定的测试。 结果是非常脆弱的测试:只要你修改或重构类,那么你很可能还需要修改单元测试。 这导致单元测试的维护成本过高。

为了避免过度指定的测试,请专注于公共方法。 如果此方法调用类中的其他方法,请不要测试这些调用。 另一方面:应测试对其他依赖组件(DOC)的方法调用。

如果你坚持这一点并且感觉你在测试中错过了一些重要的东西,那么它可能是一个类或方法做得太多的标志。 如果是班级:寻找违反单一责任原则(SRP)的行为 从中提取类并单独测试它们。 在方法的情况下:在几个公共方法中将方法拆分并分别测试每个方法。 如果这仍然太尴尬,你肯定有一个违反SRP的类。

在您的特定情况下,您可以执行以下操作:将ExportExcelDocToDataSetGetDataTableFromExcelSheet方法提取到两个不同的类中(可以将它们ExcelToDataSetExporterExcelSheetToDataTableExporter )。 包含这两种方法的原始类应该引用这两个类并调用之前提取的那些方法。 现在,您可以单独测试所有三个类。 应用Extract Class重构book )来实现原始类的修改。

另请注意,编写和维护时,改装测试总是有点麻烦。 原因是没有进行单元测试而编写的SUT往往设计笨拙,因此难以测试。 这意味着单元测试的问题必须通过修改SUT来解决,并且不能通过拉伸单元测试来解决。

它并不真正重要的引擎盖下是什么测试方法调用-这是实现细节和单元测试应该不会有太大意识到这一点。 通常(好吧,大部分时间用于单元测试)你想测试单个单元并专注于它。

您可以为类中的每个公共方法编写单独的隔离测试,也可以在外部重构测试类的部分功能。 两种方法都集中在同一个方面 - 对每个单元进行隔离测试。

现在,给你一些提示:

  • 您测试的课程的名称是什么? 基于它暴露的方法,有些东西是ExcelExporterAndToDataSetConverter ...还是ExcelManager 看来这个班级可能会同时做太多事情 ; 这要求进行一些重构。 将数据导出到DataSet可以轻松地将Excel数据转换为DataSets / DataRows。
  • GetDataTableFromExcelSheet方法发生变化时会发生什么? 被移动到其他类或被第三方代码取代? 它应该打破你的出口测试吗? 它不应该 - 这是导出测试不应该验证它是否被调用的原因之一。

我建议转移到DataSet / DataRow转换方法来分离类 - 它将简化单元测试的编写,并且您的导出测试不会那么脆弱。

我猜您是分别测试公共方法GetDataTableFromExcelSheet ,因此对于ExportExcelDocToDataSet的测试,您不需要验证GetDataTableFromExcelSheet的行为(超出ExportExcelDocToDataSet按预期工作的事实)。

常见的策略是仅测试公共方法,因为如果公共方法按预期运行,则默认情况下会测试支持公共方法的任何私有方法。

更进一步,您可以只测试一个类的行为,而不是将方法作为单位。 这有助于防止您的测试变得脆弱 - 改变课程内部的倾向会破坏您的一些测试。

当然,您希望所有代码都经过充分测试,但过于专注于方法会导致脆弱; 测试类行为(它是否在最高级别执行它应该执行的操作)也测试较低级别。

如果你想从测试中伪造方法,你可以重构代码,为你想要伪造的方法创建一个接口。 请参阅命令模式

在这种情况下,虽然明显的变化是ExportExcelDocToDataSet将工作簿作为参数。 在测试中,您可以发送假工作簿。 控制反转

有一件事是肯定你正在以正确的方式做TDD :)在上面的代码中你必须在测试ExportExcelDocToDataSet方法之前模拟方法GetDataTableFromExcelSheet。

但是,您可以做的一件事是通过添加另一个参数,从您调用ExportExcelDocToDataSet方法的代码中的位置传递从GetDataTableFromExcelSheet返回的数据表。

这样的事情

DataTable dtExcelData = GetData ....; 并修改如下方法

public DataSet ExportExcelDocToDataSet(bool headerRowProvided,DataTable dtExcelData)

这样,在测试ExportExcelDocToDataSet方法时,您无需在ExportExcelDocToDataSet方法内部模拟GetDataTableFromExcelSheet。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM