使用 OpenFileDialog c 的单元测试文件读取方法#

Unit test file reading method with OpenFileDialog c#

提问人:AlIon 提问时间:4/10/2017 最后编辑:NkosiAlIon 更新时间:4/10/2017 访问量:4839

问:

我有一个函数,它返回文本文件路径和文件内容:

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;
}

如何对文件读取进行单元测试?是否可以测试对话显示?

C# 单元测试 元组 openfiledialog 文件读取

评论

0赞 Lucero 4/10/2017
对于单元测试,请模拟对话框。对于集成测试,您可以使用一些 UI 脚本工具,该工具也会测试对话框本身。
1赞 P. Kouvarakis 4/10/2017
您需要将文件读取与对话框分开才能进行单元测试。
0赞 AlIon 4/10/2017
@Lucero谢谢。可能是UI脚本工具的例子吗?
0赞 AlIon 4/10/2017
@GrantWinney 我想测试文件打开和读取。
1赞 Lucero 4/10/2017
有很多这样的工具,如Testcomplete或Ranorex等 - 向Google询问更多;-)

答:

1赞 Steffen Cole Blake 4/10/2017 #1

是的,您可以创建一个测试文件以在测试项目中读取数据。我通常把这样的东西放在一个名为 Assets 的文件夹中。

然后,您只需指定测试文件位置的确切路径,并将其传递到函数中,并像往常一样使用断言对其进行验证,而不是 OpenFileDialog。

但是,我认为此功能不需要单元测试。它的作用非常明确。

我觉得你对这个函数返回的元组所做的就是你应该测试的内容,在这种情况下,你的单元测试应该只是手动创建元组,并用它做你的逻辑。

IE 您的测试可能会从以下方面开始:

TestFunction() {
    var TestString = "My Test data";
    var testTuple = new Tuple.Create(TestString, "Name");
    Assert.That(MyTupleLogic(testTuple), Is.Whatever());
}

您实际上不需要测试 Microsoft 的 Tuple.Create 和 OpenFileDialog 是否正常工作,这就是您建议的测试将要检查的内容。

我只费心在具有我输入的逻辑的函数上运行单元测试

2赞 Keith Nicholas 4/10/2017 #2

您希望抽象出 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

6赞 Nkosi 4/10/2017 #3

这种方法与多个问题紧密耦合。这是一个 UI 问题,也是一个 IO 问题。这使得单独测试该方法的功能变得困难,但并非不可能。OpenFileDialogFile

将这些关注点提取到它们自己的抽象中。

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;
    }
}

然后,依赖项的实现将包装它们各自的关注点。

这也将允许在单独测试它们的依赖项时模拟/替换所有抽象。

下面是基于上述建议使用 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);
}
1赞 python_kaa 4/10/2017 #4

您的方法有两个问题:在 UI 中选择文件并读取其内容。

  • 所以一开始你最好把它分成和.SelectTextFileReadAllText
  • 然后看到第二个实际上没有你定义的逻辑,而只有一个 .NET 调用。不确定如果你测试它,你是否会有更多的价值。
  • 其次,你在这里有两个边界:UI 和文件系统,它们不受你的控制。如果你有很多时间,并且想用单元测试覆盖近 100% 的代码,那么,就像其他答案中已经提到的那样,你可以将它们抽象在一些接口方法后面:IFileDialog.SelectTextFileIFileSystem.ReadAllText
  • 使用您选择的模拟库,例如最小起订量,或直接实现的值来模仿一些测试用例