具有构造函数的依赖项的单元测试

本文关键字:单元测试 依赖 构造函数 | 更新日期: 2023-09-27 18:24:54

我需要了解如何对这个方法进行单元测试。问题是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);
        }
    }

具有构造函数的依赖项的单元测试

您似乎只是在使用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; }
}

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

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

似乎没有其他选择,除了可能你应该在这里审查你的设计。如果类很难测试,这可能是设计糟糕的标志。

当我不得不在测试中创建复杂的对象时,我使用了测试数据生成器模式。举个例子,假设您有五个值要传递给构造函数:

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,它在过去对我来说很好。

这里的要点是,复杂的构造函数可以用构造函数抽象出来,这将使您能够集中精力测试业务逻辑,而不是在每次测试中都满足构造函数的要求。