Sha*_*man 60 c# file-io nunit unit-testing dependency-injection
通过Stack Overflow上的现有单元测试相关线程读取,我找不到一个有关如何单元测试文件I/O操作的明确答案.我最近才开始研究单元测试,之前已经意识到了这些优点,但是很难习惯先编写测试.我已经设置了我的项目来使用NUnit和Rhino Mocks,虽然我理解它们背后的概念,但我在理解如何使用模拟对象方面遇到了一些麻烦.
具体来说,我有两个问题需要回答.首先,单元测试文件I/O操作的正确方法是什么?其次,在我尝试学习单元测试时,我遇到了依赖注入.在Ninject设置和工作之后,我想知道我是否应该在单元测试中使用DI,或者直接实例化对象.
Rya*_*ndy 43
测试文件系统时不一定要做一件事.事实上,根据具体情况,您可能会做一些事情.
你需要问的问题是:我在测试什么?
文件系统工作吗? 您可能不需要测试它,除非您使用的是您非常不熟悉的操作系统.因此,如果您只是简单地给出一个命令来保存文件,那么编写测试以确保它们真正节省是浪费时间.
文件保存到正确的位置? 那么,你怎么知道正确的地方是什么?大概你有代码将路径与文件名组合在一起.这是您可以轻松测试的代码:您的输入是两个字符串,您的输出应该是一个字符串,它是使用这两个字符串构造的有效文件位置.
你从目录中获得了正确的文件集? 您可能必须为真正测试文件系统的file-getter类编写测试.但是你应该使用一个测试目录,其中的文件不会改变.您还应该将此测试放在集成测试项目中,因为这不是真正的单元测试,因为它取决于文件系统.
但是,我需要对我得到的文件做些什么. 对于该测试,您应该使用假的文件获取类.你的假冒应该返回一个硬编码的文件列表.如果您使用真正的文件获取器和真正的文件处理器,您将不知道哪一个导致测试失败.因此,在测试中,您的文件处理器类应该使用伪文件 - getter类.您的文件处理器类应该采用file-getter 接口.在实际代码中,您将传入真正的文件获取器.在测试代码中,您将传递一个返回已知静态列表的伪文件获取器.
基本原则是:
Vad*_*dim 35
查看使用Rhino Mocks和SystemWrapper的TDD教程.
SystemWrapper包含许多System.IO类,包括File,FileInfo,Directory,DirectoryInfo,.... 您可以看到完整列表.
在本教程中,我将展示如何使用MbUnit进行测试,但它与NUnit完全相同.
你的测试看起来像这样:
[Test]
public void When_try_to_create_directory_that_already_exists_return_false()
{
var directoryInfoStub = MockRepository.GenerateStub<IDirectoryInfoWrap>();
directoryInfoStub.Stub(x => x.Exists).Return(true);
Assert.AreEqual(false, new DirectoryInfoSample().TryToCreateDirectory(directoryInfoStub));
directoryInfoStub.AssertWasNotCalled(x => x.Create());
}
Run Code Online (Sandbox Code Playgroud)
Mar*_*son 11
Q1:
你有三个选择.
选项1:与之共存.
(没有例子:P)
选项2:在需要时创建一个轻微的抽象.
您可以更改它,而不是在测试方法中执行文件I/O(File.ReadAllBytes或其他),以便在外部完成IO并传递流.
public class MyClassThatOpensFiles
{
public bool IsDataValid(string filename)
{
var filebytes = File.ReadAllBytes(filename);
DoSomethingWithFile(fileBytes);
}
}
Run Code Online (Sandbox Code Playgroud)
会成为
// File IO is done outside prior to this call, so in the level
// above the caller would open a file and pass in the stream
public class MyClassThatNoLongerOpensFiles
{
public bool IsDataValid(Stream stream) // or byte[]
{
DoSomethingWithStreamInstead(stream); // can be a memorystream in tests
}
}
Run Code Online (Sandbox Code Playgroud)
这种方法是一种权衡.首先,是的,它更可测试.但是,它将可测试性换成了复杂性.这可能会影响可维护性和您必须编写的代码量,而且您可能只是将测试问题提升一级.
但是,根据我的经验,这是一个很好的,平衡的方法,因为您可以概括并使可测试的重要逻辑,而无需将自己提交给完全包装的文件系统.也就是说,你可以概括你真正关心的部分,而剩下的就是原样.
选项3:包装整个文件系统
更进一步,模拟文件系统可能是一种有效的方法; 这取决于你愿意忍受多少膨胀.
我以前走过这条路; 我有一个包装文件系统实现,但最后我删除了它.API中存在细微差别,我不得不在任何地方注入它,最终它会带来额外的痛苦,因为许多使用它的课程对我来说并不是非常重要.如果我一直在使用IoC容器或者写一些关键的东西并且测试需要快速,我可能会坚持使用它.与所有这些选项一样,您的里程可能会有所不同.
至于你的IoC容器问题:
手动注入测试双打.如果您需要进行大量重复性工作,只需在测试中使用setup/factory方法即可.使用IoC容器进行测试将极其恶劣!不过,也许我不理解你的第二个问题.
我使用System.IO.Abstractions
NuGet 包。
该网站有一个很好的示例,向您展示如何使用注入进行测试。 http://dontcodetired.com/blog/post/Unit-Testing-C-File-Access-Code-with-SystemIOAbstractions
这是从网站复制的代码副本。
using System.IO;
using System.IO.Abstractions;
namespace ConsoleApp1
{
public class FileProcessorTestable
{
private readonly IFileSystem _fileSystem;
public FileProcessorTestable() : this (new FileSystem()) {}
public FileProcessorTestable(IFileSystem fileSystem)
{
_fileSystem = fileSystem;
}
public void ConvertFirstLineToUpper(string inputFilePath)
{
string outputFilePath = Path.ChangeExtension(inputFilePath, ".out.txt");
using (StreamReader inputReader = _fileSystem.File.OpenText(inputFilePath))
using (StreamWriter outputWriter = _fileSystem.File.CreateText(outputFilePath))
{
bool isFirstLine = true;
while (!inputReader.EndOfStream)
{
string line = inputReader.ReadLine();
if (isFirstLine)
{
line = line.ToUpperInvariant();
isFirstLine = false;
}
outputWriter.WriteLine(line);
}
}
}
}
}
using System.IO.Abstractions.TestingHelpers;
using Xunit;
namespace XUnitTestProject1
{
public class FileProcessorTestableShould
{
[Fact]
public void ConvertFirstLine()
{
var mockFileSystem = new MockFileSystem();
var mockInputFile = new MockFileData("line1\nline2\nline3");
mockFileSystem.AddFile(@"C:\temp\in.txt", mockInputFile);
var sut = new FileProcessorTestable(mockFileSystem);
sut.ConvertFirstLineToUpper(@"C:\temp\in.txt");
MockFileData mockOutputFile = mockFileSystem.GetFile(@"C:\temp\in.out.txt");
string[] outputLines = mockOutputFile.TextContents.SplitLines();
Assert.Equal("LINE1", outputLines[0]);
Assert.Equal("line2", outputLines[1]);
Assert.Equal("line3", outputLines[2]);
}
}
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
38639 次 |
最近记录: |