【问题标题】:Unit test file reading method with OpenFileDialog c#使用OpenFileDialog c#的单元测试文件读取方法
【发布时间】:2017-09-04 21:03:24
【问题描述】:

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

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

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

【问题讨论】:

  • 对于单元测试,模拟对话框。对于集成测试,您可以使用一些 UI 脚本工具来测试对话框本身。
  • 您需要将文件读取与对话框分开才能进行单元测试。
  • @Lucero 谢谢。可能是 UI 脚本工具的示例?
  • @GrantWinney 我想测试文件的打开和读取。
  • 有很多这样的工具,例如 Testcomplete 或 Ranorex 等 - 更多信息请咨询 Google ;-)

标签: c# unit-testing tuples openfiledialog file-read


【解决方案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】:

    您希望抽象出 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 之类的模拟框架进行测试

    【讨论】:

      【解决方案3】:

      该方法与多个关注点紧密耦合。 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;
          }
      }
      

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

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

      以下是基于上述建议使用 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);
      }
      

      【讨论】:

        【解决方案4】:

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

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

        【讨论】:

          猜你喜欢
          • 2016-11-12
          • 2019-05-11
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-01-09
          • 1970-01-01
          • 2013-10-11
          相关资源
          最近更新 更多