【问题标题】:How to create unit test for method that reads file and has file path as parameter如何为读取文件并将文件路径作为参数的方法创建单元测试
【发布时间】:2016-06-13 16:04:24
【问题描述】:

我知道这可能很容易,但是由于我刚刚开始学习单元测试,所以我对所有可能性都有些困惑。

我有一个非常简单的方法,它应该从文件中读取整数(条件是文件只包含整数,每个都存储在一个新行中)。

public static IEnumerable<int> ReadIntegers(string file)
{
    using (var stream = File.OpenText(file))
    {
        string line;
        while ((line = stream.ReadLine()) != null)
        {
            yield return int.Parse(line);
        }
    }
}

在我继续之前,请不要建议我重构方法,我已经找到了,我想学习如何为这个特定的输入参数和输出值编写单元测试。

我在另一个主题上发现有人建议在测试用例中创建一个测试文件,而不是尝试将预期结果与实际结果进行比较,如下所示:

 [Test]
 public void ShouldValidateReadingOfTheFile()
 {
     string path = @"E:\TestFile.txt";
     using (var sw = File.AppendText(path))
     {
         sw.WriteLine(1);
         sw.WriteLine(100);
     }
     var expected = new List<int>() { 1, 100 };
     var actual = TestProject.Merge.ReadIntegersFromFile(path);
     Assert.IsTrue(expected.SequenceEqual(actual));
 }

我想问你这是一种不好的做法吗?你有不同的方法吗?对于这种方法的不同测试还有其他想法吗?谢谢!

【问题讨论】:

  • 您表示您不想重构,但在同一情况下询问这是否是一种不好的做法以及是否有不同的方法。您可以抽象服务后面读取的 File 行,以便您可以模拟它以进行测试。并让实际实现从文件中获取数据。尝试像提供的建议那样分离您的方法的职责。它试图做的太多了。
  • 始终避免来自单元测试的硬编码文件访问(最好完全避免它)。您可能在另一台机器上没有 F 盘。有些甚至没有 C 盘或没有剩余空间。 Luiso 的内存流是一个很好的解决方案。当它确实需要成为一个文件时,您也可以不惜任何代价使用部署项。使用模拟将是另一种解决方案。

标签: c# unit-testing nunit


【解决方案1】:

我认为您可以通过这种方式执行unit-testing,但我建议您也测试特殊情况。

例如

  • null 作为文件名
  • 一个空文件
  • 一个包含许多条目的大文件
  • 其中不仅包含整数的文件

创建测试文件是一种常见的做法。

【讨论】:

    【解决方案2】:

    我会将方法拆分为 2

    public static IEnumerable<int> ReadIntegers(Stream file)
    {
        ///... your code here
    }
    

    public static IEnumerable<int> ReadIntegers(string file)
    {
         var stream = File.OpenText(file);
         return ReadIntegers(stream)
    }
    

    对于单元测试本身:

    [Test]
    public void ShouldValidateReadingOfTheFile()
    {
        string stream = new MemoryStream();
        using (var sw = StreamWiter(stream))
        {
            sw.WriteLine(1);
            sw.WriteLine(100);
        }
        var expected = new List<int>() { 1, 100 };
        var actual = TestProject.Merge.ReadIntegersFromFile(path);
        Assert.IsTrue(expected.SequenceEqual(actual));
    }
    

    我所做的是将MemoryStreamStreamWriter 包装起来,这样就很容易编写行并填充我想要的值。应该这样做。

    然后您可以使用相同的策略来测试所有其他情况并制作这样的帮助程序:

    private Stream GetStream(IEnumerable<int> values)
    {
        string stream = new MemoryStream();
        using (var sw = StreamWiter(stream))
        {
            foreach(int i in values)
                sw.WriteLine(i);
        }
    
        return stream;
    }
    

    或类似的东西。使用这种方法,您可以对执行实际工作的方法(带有Stream 参数的方法)进行所有您想要的测试,如果您仍然需要测试文件打开(stringStream)代码然后你可以在你的项目中使用一个测试文件,这样你就可以测试它,虽然我看不出它的意义,因为你要测试框架处理文件的能力,如果文件名来自你的代码,那么最好改为测试文件名生成代码。希望这可以帮助。快乐编码

    【讨论】:

      【解决方案3】:

      您可以使用typemock 框架对您的方法进行单元测试,而无需对其进行重构,这是您的代码示例:

      [TestMethod, Isolated]
      public void ShouldValidateReadingOfTheFile()
      {
          var fileContents = 
      @"1
      100";
          StreamReader reader = new StreamReader(
              new MemoryStream(Encoding.ASCII.GetBytes(fileContents)));
      
          Isolate.WhenCalled(() => File.OpenText("fake")).WillReturn(reader);
      
          var result = Class1.ReadIntegers("fake").ToArray();
      
          var resList = new int[] {1, 100};
          CollectionAssert.AreEquivalent(resList, result);
      }
      

      我希望这将帮助您更好地理解单元测试。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2023-03-18
        • 2016-11-12
        • 1970-01-01
        • 2017-04-13
        • 2020-04-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多