c# - 当类相互依赖或外部数据时如何使用单元测试?

标签 c# .net unit-testing visual-studio-2010 tdd

我想开始使用单元测试,但我很难理解如何将它们用于我当前的项目。

我当前的项目是一个将文件收集到“目录”中的应用程序。 Catalog 然后可以从它包含的文件中提取信息,例如缩略图和其他属性。用户还可以使用其他自定义元数据(例如“作者”和“注释”)标记文件。它可以很容易地与 Picasa 或 Adob​​e Lightroom 等相册应用程序进行比较。

我已将用于创建和操作Catalog 的代码分离到一个单独的 DLL 中,我现在想对其进行测试。然而,我的大部分类从来都不打算自己实例化。相反,一切都通过我的 Catalog 类发生。例如,我无法单独测试我的 File 类,因为 File 只能通过 Catalog 访问。

作为单元测试的替代方案,我认为编写一个运行一系列操作(包括创建目录、重新打开已创建的目录以及操作目录的内容)的测试程序对我来说更有意义目录。请参阅下面的代码。

//NOTE: The real version would have code to log the results and any exceptions thrown

//input data
string testCatalogALocation = "C:\TestCatalogA"
string testCatalogBLocation = "C:\TestCatalogB"
string testFileLocation = "C:\testfile.jpg"
string testFileName = System.IO.Path.GetFileName(testFileLocation);


//Test creating catalogs
Catalog catAtemp = Catalog(testCatalogALocation)
Catalog catBtemp = Catalog(testCatalogBLocation );


//test opening catalogs
Catalog catA = Catalog.OpenCatalog(testCatalogALocation);
Catalog catB = Catalog.OpenCatalog(testCatalogBLocation );


using(FileStream fs = new FileStream(testFileLocation )
{
    //test importing a file
    catA.ImportFile(testFileName,fs);
}

//test retrieving a file
File testFile = catA.GetFile(System.IO.Path.GetFileName(testFileLocation));

//test copying between catalogs
catB.CopyFileTo(testFile);


//Clean Up after test
System.IO.Directory.Delete(testCatalogALocation);
System.IO.Directory.Delete(testCatalogBLocation);

首先,我是否遗漏了什么?有什么方法可以对这样的程序进行单元测试吗?其次,是否有某种方法可以像上面的代码一样创建过程类型测试,但又能够利用构建到 Visual Studio 中的测试工具? VS2010 中的“通用测试”是否允许我这样做?


更新

感谢大家的所有回复。实际上我的类确实继承自一系列接口(interface)。 Here's a class diagram对于任何有兴趣的人。实际上我有更多的接口(interface)然后我有类。为了简单起见,我只是省略了示例中的接口(interface)。

感谢所有使用模拟的建议。我过去听说过这个词,但直到现在才真正理解什么是“模拟”。我了解如何创建 IFile 接口(interface)的模拟,它代表目录中的单个文件。我还了解如何创建我的 ICatalog 界面的模拟版本来测试两个目录如何交互。

但我不明白如何测试我的具体 ICatalog 实现,因为它们与其后端数据源密切相关。实际上,我的目录类的全部目的是读取、写入和操作它们的外部数据/资源。

最佳答案

你应该阅读 SOLID代码原则。特别是 SOLID 上的“D”代表 Dependency Injection/Inversion Principle ,这是您要测试的类不依赖于其他具体类和外部实现,而是依赖于接口(interface)和抽象的地方。您依靠 IoC(控制反转)容器(例如 UnityNinjectCastle Windsor)在运行时动态注入(inject)具体依赖项,但在单元测试期间,您注入(inject)的是模拟/ stub 。

例如考虑以下类:

public class ComplexAlgorithm
{
    protected DatabaseAccessor _data;

    public ComplexAlgorithm(DatabaseAccessor dataAccessor)
    {
        _data = dataAccessor;
    }

    public int RunAlgorithm()
    {
        // RunAlgorithm needs to call methods from DatabaseAccessor
    }
}

RunAlgorithm() 方法需要访问数据库(通过 DatabaseAccessor),因此难以测试。因此,我们将 DatabaseAccessor 更改为接口(interface)。

public class ComplexAlgorithm
{
    protected IDatabaseAccessor _data;

    public ComplexAlgorithm(IDatabaseAccessor dataAccessor)
    {
        _data = dataAccessor;
    }

    // rest of class (snip)
}

现在,ComplexAlgorithm 依赖于一个接口(interface) IDatabaseAccessor,当我们需要单独对 ComplexAlgorithm 进行单元测试时,可以很容易地对其进行模拟。例如:

public class MyFakeDataAccessor : IDatabaseAccessor
{
    public IList<Thing> GetThings()
    {
        // Return a fake/pretend list of things for testing
        return new List<Thing>()
        {
            new Thing("Thing 1"),
            new Thing("Thing 2"),
            new Thing("Thing 3"),
            new Thing("Thing 4")
        };
    }

    // Other methods (snip)
}

[Test]
public void Should_Return_8_With_Four_Things_In_Database()
{
    // Arrange
    IDatabaseAccessor fakeData = new MyFakeDataAccessor();
    ComplexAlgorithm algorithm = new ComplexAlgorithm(fakeData);
    int expectedValue = 8;

    // Act
    int actualValue = algorithm.RunAlgorithm();

    // Assert
    Assert.AreEqual(expectedValue, actualValue);
}

我们实质上是将这两个类相互“解耦”。解耦是另一个重要的软件工程原则,用于编写更易于维护和更健壮的代码。

就依赖注入(inject)、SOLID 和解耦而言,这确实是冰山一角,但这是有效地对代码进行单元测试所需要的。

关于c# - 当类相互依赖或外部数据时如何使用单元测试?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3664415/

相关文章:

c# - WPF 高频数据绑定(bind)

c# - 是否可以在 winforms 应用程序中为 MDI 子级使用 Aero Peek?

c# - 您可以在静态上下文中引用非静态方法吗?

c# - .Net规则引擎

.net - .Net 中的异步文件 IO

c# - ASP.NET c# 在每次单击按钮时按天数增加日期值

.net - 在运行时枚举 .NET 程序集资源

python - Django测试客户端-发送POST数据返回400错误

unit-testing - hbase-testing-utility 的 sbt 依赖管理问题

android - 在 Android Studio 中将集成测试与单元测试分开