【问题标题】:Unit Testing with dependencies that have constructors使用具有构造函数的依赖项进行单元测试
【发布时间】:2013-07-25 20:31:31
【问题描述】:

我需要一些如何对这个方法进行单元测试。问题是 FsFileGroupFile 不容易模拟,它具有复杂的构造函数要求并且不直接使用接口。另一方面,_blockReaderFactory 是一个接口,因此很容易模拟。我怎么能模拟这么复杂的对象。我正在使用 Rhino Mocks 和 Microsoft 单元测试框架。有人有什么想法吗?

    public void ReadGeneral(FsFileGroupFile a_file, FileItemData a_fileItemData)
    {
        try
        {
            var blockReader = _blockReaderFactory.Create(a_file.File.FullName, "CabinetData/StartData");

            var version = blockReader.ReadVersion();
            var name = blockReader.ReadString();
            var type = blockReader.ReadString();
            var defaultHeight = blockReader.ReadDouble();
            var defaultWidth = blockReader.ReadDouble();
            var defaultDepth = blockReader.ReadDouble();

            a_fileItemData.Name = name;
            a_fileItemData.DefaultWidth = defaultWidth * 100.0;
            a_fileItemData.DefaultHeight = defaultHeight * 100.0;
            a_fileItemData.DefaultDepth = defaultDepth * 100.0;
        }
        catch (Exception ex)
        {
            throw new IOException("General data could not be read from block data.", ex);
        }
    }

【问题讨论】:

    标签: c# unit-testing


    【解决方案1】:

    您似乎只使用a_file 来获取文件名。那么为什么不创建一个接口FilenameSupplier(或类似的),并编写一个实现它的包装器呢?

    Java 代码示例(在问题标记为 C#...之前添加):

    interface FilenameSupplier {
        String getName();
    }
    
    public void ReadGeneral(FilenameSupplier a_file, FileItemData a_fileItemData) {
        ...
        a_file.getName();
        ...
    }
    
    class ConcreteSupplier implements FilenameSupplier {
        private final FsFileGroupFile file;
        public ConcreteSupplier(FsFileGroupFile file) { this.file = file; }
        String getName() { return a_file.File.FullName; }
    }
    

    【讨论】:

    • 我使用的是 C# 而不是 Java。很抱歉我没有在帖子里这么说。我想这就是我要做的。有一个只用于测试的接口似乎很奇怪。但我已经筋疲力尽了。
    • @Jordan:嗯,这就是在基于反射的模拟框架普及之前所做的;)我对 Rhino(或 C#)不够熟悉,不知道是否有更好的方法来实现这一点,恐怕……
    【解决方案2】:

    您应该从FsFileGroupFile 中提取一些接口并将其传递给构造函数参数。 然后,您可以轻松地使用您喜欢的框架模拟这个界面,在您的案例中,Rhino Mocks。

    如果不合适,您应该构建您的FsFileGroupFile,并且在其复杂的构造函数中传递参数时可能会使用模拟。

    似乎没有其他选择,除非您应该在这里查看您的设计。如果类很难测试,则可能是设计不佳的标志。

    【讨论】:

      【解决方案3】:

      当我不得不在测试中创建复杂的对象时,我使用了Test Data Builder Pattern。例如,假设您有五个值要传递给构造函数:

      public FsFileGroupFile(string firstProperty, string secondProperty,
          string thirdProperty, string fourthProperty, string fifthProperty)
      {
          // constructor logic goes here
      }
      

      这将在单元测试项目中使用测试构建器类进行包装:

      public class FsFileGroupFileBuilder
      {
          public string FirstProperty { get; set; }
          public string SecondProperty { get; set; }
          public string ThirdProperty { get; set; }
          public string FourthProperty { get; set; }
          public string FifthProperty { get; set; }
      
          public FsFileGroupFile Build()
          {
              return new FsFileGroupFile(FirstProperty, SecondProperty, ThirdProperty,
                  FourthProperty, FifthProperty);
          }
      }
      

      现在您可以只为您关心的属性赋值并以这种方式构建您的对象:

      // in your test setup use this to initial to a default/valid state
      var fsFileGroupBuilder = new fsFileGroupBuilder
      {
          FirstProperty = "Default",
          SecondProperty = "Default",
          ThirdProperty = "Default",
          FourthProperty = "Default",
          FifthProperty = "Default"
      }
      

      注意:Rhino Mocks 可能可以为您设置这些默认值,但我没有亲自使用过,所以不确定。

      // Override the properties in each test
      fsFileGroupBuilder.ThirdProperty = "Value needed for unit test."
      
      // create
      var fileItemData = new FileItemData();
      ReadGeneral(fsFileGroupBuilder.Build(), fileItemData);
      

      还有其他开源库可以帮助生成测试数据,例如 NBuilder,这些库过去对我很有效。

      这里的要点是复杂的构造函数可以用一个构建器抽象出来,这样你就可以专注于测试你的业务逻辑,而不是在每个测试中都满足构造函数。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-05-10
        • 2011-05-19
        • 1970-01-01
        • 2020-12-03
        • 2012-06-28
        • 2017-09-03
        • 1970-01-01
        相关资源
        最近更新 更多