10

我正在努力养成编写单元测试的习惯,我之前写过一些,但它们通常都很基础......我想开始转向 TDD,因为我想提高质量我的代码(设计和结构) - 减少耦合,同时希望减少滑入可测试构建的回归数量。

我开始着手一个相对简单的项目。生成的程序监视一个文件夹,然后对该文件夹中的文件进行操作。

这是从项目中提取的一些代码的典型示例:

private string RestoreExtension(String file)
    {
        var unknownFile = Path.GetFileName(file);
        var ignoreDir = Path.GetDirectoryName(file) + "\\Unknown";
        string newFile;

        //We need this library for determining mime
        if (CheckLibrary("urlmon.dll"))
        {
            AddLogLine("Attempting to restore file extension");
            var mime = GetMimeType(BufferFile(file, 256));
            var extension = FileExtensions.FindExtension(mime);
            if (!String.IsNullOrEmpty(extension))
            {
                AddLogLine("Found extension: " + extension);
                newFile = file + "." + extension;
                File.Move(file, newFile);
                return newFile;
            }
        }
        else
        {
            AddLogLine("Unable to load urlmon.dll");
        }

        AddLogLine("Unable to restore file extension");

        if (!Directory.Exists(ignoreDir))
        {
            Directory.CreateDirectory(ignoreDir);
        }
        var time = DateTime.Now;
        const string format = "dd-MM-yyyy HH-mm-ss";

        newFile = ignoreDir + "\\" + unknownFile + "-" + time.ToString(format);
        File.Move(file, newFile);

        return String.Empty;
    }

问题 :

如何使用 IO 测试方法?我真的不想使用真正的文件,因为这会很慢(而且更多的是集成测试?),但我真的看不到另一种方式。我可以添加一个可切换的 IO 抽象层,但这听起来像是不必要地使代码复杂化......

这样的项目值得进行单元测试吗?我的意思是,是不是太简单了。一旦你去掉 .Net 和 3rd 方库调用,就剩下不多了……那么,使它可测试的工作量意味着它不是一个很好的测试候选者吗?

这个项目中的很多方法都是私有的,因为这个项目恰好是一个 Windows 服务。我读过你应该只测试外部可见的方法(公共或通过接口公开),在这种情况下不太可能但是我想公开我自己的任何方法。那么这个项目是否值得测试(因为 id 可能需要将方法访问修饰符更改为 public 或添加一些属性以便 NUnit 可以看到它们)?

4

4 回答 4

10

关于如何测试文件 I/O:执行此操作的常用方法是将文件 I/O 封装在包装器类中。例如:

public class FileHandler : IFileHandler
{
     public string GetFilename(string name)
     {
         return System.IO.GetFileName(name);
     }

    public string GetDirectoryName(string directory)
    {
         return System.IO.GetDirectoryName(directory);
    }
}

public interface IFileHandler
{
      string GetFilename(string name);
      string GetDirectoryName(string directory);
}

在您的测试方法中,您仅针对接口进行编程。当您运行单元测试时,您将使用一个返回预定义值的模拟对象。为此,您可以使用Moq

这需要做一些工作,但是您也不需要测试文件 I/O。

最佳一月

于 2013-05-14T11:49:04.677 回答
7

如何使用 IO 测试方法?

通过在 .NET 的 IO 库上引入抽象,然后在应用程序中使用真实实现 (BCL),并在单元测试中使用假(模拟)实现。这样的抽象很容易自己编写,但是那些项目已经存在(例如System.IO.Abstraction),所以重新发明轮子是没有意义的,尤其是在这样微不足道的事情上。

我真的不想使用真实文件,因为这会很慢

这是正确的,在测试中你想使用模拟)对象

这样的项目值得进行单元测试吗?

每个项目都值得测试,您将以一种或另一种方式对其进行测试(在最极端的情况下,通过手动执行应用程序并进行良好的、旧的点击等待测试)。

请注意,手动测试复杂解析器方法的 10 个边缘情况几乎总是太耗时,每次都需要您加载大文件并等待结果。这就是隔离环境中的自动化测试有很大帮助的地方。您的代码也是如此——文件系统、Web 服务,它们都会在您进入实际业务逻辑之前引入大量开销。必须隔离。

这个项目中的很多方法都是私有的(...)我读过你应该只测试外部可见的方法

再次正确,您应该测试公共合同。然而,如果私有的东西变得足够大来测试它,大多数时候它是一个很好的指标,它也足够大到可以把它放在自己的类中。请注意,仅为单元测试使用更改成员访问修饰符并不是最好的主意。几乎总是提取类/方法重构更好。

于 2013-05-14T13:38:03.040 回答
1

您可以使用Microsoft Fakes来做到这一点。首先为 System.dll 或任何其他包生成一个假程序集,然后模拟预期返回,如下所示:

using Microsoft.QualityTools.Testing.Fakes;
...
using (ShimsContext.Create())
{
     System.IO.Fakes.ShimDirectory.ExistsString = (p) => true;
     System.IO.Fakes.ShimFile.MoveStringString = (p,n) => {};
     // .. and other methods you'd like to test

     var result = RestoreExtension("C:\myfile.ext");
     // then you can assert with this result 
}
于 2018-01-25T02:57:18.040 回答
-1

我对 TDD 也很陌生,但我想我可以给你一些建议:

  1. 关于 IO 测试。您可以使用模拟对象。为您要测试的方法创建一个模拟,并将其与实际传入的值进行比较。在 Moq 框架中,它看起来像这样:

    var mock = new Mock<IRepository<PersonalCabinetAccount>>();
    mock.Setup(m => m.RemoveByID(expectedID))
        .Callback((Int32 a) => Assert.AreEqual(expectedID, a));
    
    var account = new PersonalCabinetAccount {ID = myID};
    var repository = mock.Object;
    repository.RemoveByID(account.myId);
    
  2. 绝对没错。如果你想养成一个习惯,你需要测试一切,我想。这样知识就来了;)

  3. 您可以使用InternalsVisibleTo属性来避免可见性问题。

    外部可见的测试方法

我认为这是关于企业编程的。只是花时间使用您自己的受信任代码。

希望我的观点可以帮到你。

于 2013-05-14T12:19:25.610 回答