我想开始使用单元测试,但我很难理解如何将它们用于我当前的项目。
我当前的项目是一个将文件收集到“目录”中的应用程序。 Catalog
然后可以从它包含的文件中提取信息,例如缩略图和其他属性。用户还可以使用其他自定义元数据(例如“作者”和“注释”)标记文件。它可以很容易地与 Picasa 或 Adobe 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(控制反转)容器(例如 Unity、Ninject 或 Castle 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/