OpenFileDialog c#单元测试文件读取方法
Unit test file reading method with OpenFileDialog c#
我有一个函数,returns 文本文件路径和文件内容:
public static Tuple<string, string> OpenTextFile()
{
OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog .Filter = "Text |*.txt";
bool? accept = openFileDialog.ShowDialog();
if (accept == true)
return Tuple.Create(File.ReadAllText(openFileDialog.FileName, Encoding.UTF8), openFileDialog.FileName);
else
return null;
}
如何对文件读取进行单元测试?是否可以测试对话框显示?
是的,您可以创建一个测试文件以在您的测试项目中读取。我通常把这样的东西放在一个名为 Assets 的文件夹中。
然后不用 OpenFileDialog,您只需指定测试文件位置的确切路径并将其传递到您的函数中,并按照通常的方式使用断言对其进行验证。
不过我认为这个功能不需要单元测试。它的作用非常明确。
我觉得你用元组做这个函数returns是你应该测试的,在这种情况下你的单元测试应该只创建元组手,然后用它来做你的逻辑。
IE 您的测试可能会以:
TestFunction() {
var TestString = "My Test data";
var testTuple = new Tuple.Create(TestString, "Name");
Assert.That(MyTupleLogic(testTuple), Is.Whatever());
}
您实际上不需要测试 Microsoft 的 Tuple.Create 和 OpenFileDialog 是否正常工作,这正是您建议的测试要检查的内容。
我只对 具有我输入的逻辑的函数运行 进行单元测试
您不想直接测试跨越边界的 UI,而是想抽象掉 UI。
也许 :-
interface IFileSelector
{
string Filter {get; set'}
bool? SelectFile()
string FileSelected
}
然后
public static Tuple<string, string> OpenTextFile(IFileSelector selector)
{
selector.Filter = "Text |*.txt";
bool? accept = selector.SelectFile()
if (accept == true)
return Tuple.Create(File.ReadAllText(selector.FileSelected, Encoding.UTF8), selector.FileSelected);
else
return null;
}
然后做一个UI一个
public class WinformsFileSelector : IFileSelector
{
...
}
然后使用模拟框架进行测试,如 Moq
该方法与多个关注点紧密耦合。 OpenFileDialog
是一个 UI 问题,File
是一个 IO 问题。这使得单独测试该方法的功能变得困难,但并非不可能。
将这些问题提取到他们自己的抽象中。
public interface IOpenFileDialog {
string Filter { get; set; }
bool? ShowDialog();
string FileName { get; set; }
}
public interface IFileSystem {
string ReadAllText(string path, Encoding encoding = Encoding.UTF8);
}
我还建议将该静态方法转换为服务方法
public interface ITextFileService {
Tuple<string, string> OpenTextFile();
}
它的实现将取决于其他抽象
public class TextFileService : ITextFileService {
readonly IOpenFileDialog openFileDialog;
readonly IFileSystem file;
public SUT(IOpenFileDialog openFileDialog, IFileSystem file) {
this.openFileDialog = openFileDialog;
this.file = file;
}
public Tuple<string, string> OpenTextFile() {
openFileDialog.Filter = "Text |*.txt";
bool? accept = openFileDialog.ShowDialog();
if (accept.GetValueOrDefault(false))
return Tuple.Create(file.ReadAllText(openFileDialog.FileName, Encoding.UTF8), openFileDialog.FileName);
else
return null;
}
}
依赖项的实现将包装它们各自的关注点。
这也将允许所有抽象在单独测试其依赖项时成为 mocked/replaced。
这里是根据以上建议使用MSTest和Moq测试方法的例子。
[TestMethod]
public void _OpenTextFile_Should_Return_TextContext_And_FileName() {
//Arrange
var expectedFileContent = "Hellow World";
var expectedFileName = "filename.txt";
var fileSystem = new Mock<IFileSystem>();
fileSystem.Setup(_ => _.ReadAllText(expectedFileName, It.IsAny<Encoding>()))
.Returns(expectedFileContent)
.Verifiable();
var openFileDialog = new Mock<IOpenFileDialog>();
openFileDialog.Setup(_ => _.ShowDialog()).Returns(true).Verifiable();
openFileDialog.Setup(_ => _.FileName).Returns(expectedFileName).Verifiable();
var sut = new TextFileService(openFileDialog.Object, fileSystem.Object);
//Act
var actual = sut.OpenTextFile();
//Assert
fileSystem.Verify();
openFileDialog.Verify();
Assert.AreEqual(expectedFileContent, actual.Item1);
Assert.AreEqual(expectedFileName, actual.Item2);
}
您的方法有两个问题:选择 UI 中的文件并读取其内容。
- 所以一开始你最好把它分成
SelectTextFile
和 ReadAllText
。
- 然后看到第二个实际上没有您定义的逻辑,只有一个.NET 调用。不确定如果你测试它是否会有更多价值。
- 第二,这里有两个边界:UI 和文件系统,它们不受您的控制。如果您有很多时间并且想用单元测试覆盖近 100% 的代码,那么就像其他答案中已经提到的那样,您可以将它们抽象到一些接口方法后面:
IFileDialog.SelectTextFile
和 IFileSystem.ReadAllText
- 使用您选择的模拟库,例如Moq,或者直接实现值来模仿一些测试用例
我有一个函数,returns 文本文件路径和文件内容:
public static Tuple<string, string> OpenTextFile()
{
OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog .Filter = "Text |*.txt";
bool? accept = openFileDialog.ShowDialog();
if (accept == true)
return Tuple.Create(File.ReadAllText(openFileDialog.FileName, Encoding.UTF8), openFileDialog.FileName);
else
return null;
}
如何对文件读取进行单元测试?是否可以测试对话框显示?
是的,您可以创建一个测试文件以在您的测试项目中读取。我通常把这样的东西放在一个名为 Assets 的文件夹中。
然后不用 OpenFileDialog,您只需指定测试文件位置的确切路径并将其传递到您的函数中,并按照通常的方式使用断言对其进行验证。
不过我认为这个功能不需要单元测试。它的作用非常明确。
我觉得你用元组做这个函数returns是你应该测试的,在这种情况下你的单元测试应该只创建元组手,然后用它来做你的逻辑。
IE 您的测试可能会以:
TestFunction() {
var TestString = "My Test data";
var testTuple = new Tuple.Create(TestString, "Name");
Assert.That(MyTupleLogic(testTuple), Is.Whatever());
}
您实际上不需要测试 Microsoft 的 Tuple.Create 和 OpenFileDialog 是否正常工作,这正是您建议的测试要检查的内容。
我只对 具有我输入的逻辑的函数运行 进行单元测试
您不想直接测试跨越边界的 UI,而是想抽象掉 UI。
也许 :-
interface IFileSelector
{
string Filter {get; set'}
bool? SelectFile()
string FileSelected
}
然后
public static Tuple<string, string> OpenTextFile(IFileSelector selector)
{
selector.Filter = "Text |*.txt";
bool? accept = selector.SelectFile()
if (accept == true)
return Tuple.Create(File.ReadAllText(selector.FileSelected, Encoding.UTF8), selector.FileSelected);
else
return null;
}
然后做一个UI一个
public class WinformsFileSelector : IFileSelector
{
...
}
然后使用模拟框架进行测试,如 Moq
该方法与多个关注点紧密耦合。 OpenFileDialog
是一个 UI 问题,File
是一个 IO 问题。这使得单独测试该方法的功能变得困难,但并非不可能。
将这些问题提取到他们自己的抽象中。
public interface IOpenFileDialog {
string Filter { get; set; }
bool? ShowDialog();
string FileName { get; set; }
}
public interface IFileSystem {
string ReadAllText(string path, Encoding encoding = Encoding.UTF8);
}
我还建议将该静态方法转换为服务方法
public interface ITextFileService {
Tuple<string, string> OpenTextFile();
}
它的实现将取决于其他抽象
public class TextFileService : ITextFileService {
readonly IOpenFileDialog openFileDialog;
readonly IFileSystem file;
public SUT(IOpenFileDialog openFileDialog, IFileSystem file) {
this.openFileDialog = openFileDialog;
this.file = file;
}
public Tuple<string, string> OpenTextFile() {
openFileDialog.Filter = "Text |*.txt";
bool? accept = openFileDialog.ShowDialog();
if (accept.GetValueOrDefault(false))
return Tuple.Create(file.ReadAllText(openFileDialog.FileName, Encoding.UTF8), openFileDialog.FileName);
else
return null;
}
}
依赖项的实现将包装它们各自的关注点。
这也将允许所有抽象在单独测试其依赖项时成为 mocked/replaced。
这里是根据以上建议使用MSTest和Moq测试方法的例子。
[TestMethod]
public void _OpenTextFile_Should_Return_TextContext_And_FileName() {
//Arrange
var expectedFileContent = "Hellow World";
var expectedFileName = "filename.txt";
var fileSystem = new Mock<IFileSystem>();
fileSystem.Setup(_ => _.ReadAllText(expectedFileName, It.IsAny<Encoding>()))
.Returns(expectedFileContent)
.Verifiable();
var openFileDialog = new Mock<IOpenFileDialog>();
openFileDialog.Setup(_ => _.ShowDialog()).Returns(true).Verifiable();
openFileDialog.Setup(_ => _.FileName).Returns(expectedFileName).Verifiable();
var sut = new TextFileService(openFileDialog.Object, fileSystem.Object);
//Act
var actual = sut.OpenTextFile();
//Assert
fileSystem.Verify();
openFileDialog.Verify();
Assert.AreEqual(expectedFileContent, actual.Item1);
Assert.AreEqual(expectedFileName, actual.Item2);
}
您的方法有两个问题:选择 UI 中的文件并读取其内容。
- 所以一开始你最好把它分成
SelectTextFile
和ReadAllText
。 - 然后看到第二个实际上没有您定义的逻辑,只有一个.NET 调用。不确定如果你测试它是否会有更多价值。
- 第二,这里有两个边界:UI 和文件系统,它们不受您的控制。如果您有很多时间并且想用单元测试覆盖近 100% 的代码,那么就像其他答案中已经提到的那样,您可以将它们抽象到一些接口方法后面:
IFileDialog.SelectTextFile
和IFileSystem.ReadAllText
- 使用您选择的模拟库,例如Moq,或者直接实现值来模仿一些测试用例