我之前从未使用过单元测试,但我希望这样做能够继续前进。现在我有一个我想测试的最简单的方法:
public void GetAvailableFiles(string rootFolder)
{
string[] dirs = Directory.GetFiles(rootFolder);
}
我的单元测试如下:
[Test]
public void CheckDirectory()
{
AvailableFile fileUpload = new AvailableFile();
fileUpload.GetAvailableFiles("C:\\Input");
}
当我调试它时,我返回目标位置中的所有文件。但是,我正在努力想出一个有效的条件,在这个条件下,这个测试可以验证它已经通过了。
我的想法是为此测试提供预期的文件数。我有10个文件,所以我应该期待10个结果。但是,我不确定如何用我所拥有的东西来写这个。
我应该怎么做?
答案 0 :(得分:7)
这不是单元测试。这是一个集成测试,因为您与文件系统进行交互。如果要进行单元测试,则必须将文件系统交互提取到接口。例如:
public interface IFileScanner {
List<string> GetAvailableFiles(string folder);
}
[Test]
public void GetAvailableFiles_EmptyFolder_ReturnsEmptyList()
{
// Arrange
IFileScanner scanner = new FileScannerEmptyFolderStub();
// Act
var list = scanner.GetAvailableFiles("dummy argument");
// Assert
Assert.IsTrue(list.Count == 0);
}
我建议您在编写单元测试之前阅读本书The art of unit testing with examples (Roy Osherove)
中的一些理论。
答案 1 :(得分:1)
为方法提供一些临时目录,其中包含一些文件:
public class TestClass : IDisposable
{
private string _directory;
public TestClass()
{
_directory = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
Directory.CreateDirectory(_directory);
}
[Fact]
public void NoFiles()
{
Assert.Empty(GetAvailableFiles(_directory));
}
[Fact]
public void TwoFiles()
{
File.WriteAllText(Path.Combine(_directory, "aaa.txt"), "");
File.WriteAllText(Path.Combine(_directory, "bbb.txt"), "");
var files = GetAvailableFiles(_directory);
Assert.Equal(2, files.Length);
Assert.Contains(Path.Combine(_directory, "aaa.txt"), files);
Assert.Contains(Path.Combine(_directory, "bbb.txt"), files);
}
public void Dispose()
{
Directory.Delete(_directory, true);
}
// Method under test
public string[] GetAvailableFiles(string rootFolder)
{
return Directory.GetFiles(rootFolder);
}
}
这个测试很快,不需要任何依赖,因此虽然它不是严格的单元测试,但它没有带来许多通常与集成测试相关的缺点(依赖于外部数据库等)。 。)
如果此方法包含大量其他逻辑(例如,使用这些文件的内容),那么在该逻辑和文件系统访问之间引入接缝是一个好主意。
答案 2 :(得分:1)
正在测试的目标方法与静态实现问题紧密相关(Directory
)。
假设目标SUT
public class AvailableFile {
public void GetAvailableFiles(string rootFolder) {
string[] files = Directory.GetFiles(rootFolder);
//...other code using files
}
}
将其提取为显式服务依赖项。
public interface IDirectory {
string[] GetFiles(string rootFolder);
}
重构SUT
public class AvailableFile {
private readonly IDirectory directory;
public AvailableFile (IDirectory directory) {
this.directory = directory;
}
public void GetAvailableFiles(string rootFolder) {
string[] files = directory.GetFiles(rootFolder);
//...other code using files
}
}
生产中的服务实现可以像
那样完成public class DirectoryServie : IDirectory {
public string[] GetFiles(string rootFolder) {
return Directory.GetFiles(rootFolder);
}
}
确保在合成根目录中向IoC容器注册了接口和实现。
完成所有解耦后,您现在可以单独测试依赖于这些服务的实现。你真的想要测试你控制的代码而不是第三部分实现,这就是Directory
。微软会对此进行足够的测试。
使用Moq,您可以独立测试目标系统,而无需考虑实现问题。
[Test]
public void CheckDirectory_Should_GetFiles() {
//Arrange
var files = new [] {
"fake path 1",
"fake path 2",
//fake path n
};
var rootPath = "C:\\Input";
var service = new Mock<IDirectory>();
service.Setup(_ => _.GetFiles(rootPath).Returns(files).Verifiable();
var fileUpload = new AvailableFile(service.Object);
//Act
fileUpload.GetAvailableFiles(rootPath);
//Assert
service.Verify();
}