使用具有构造函数的依赖项进行单元测试

时间:2013-07-25 20:31:32

标签: c# unit-testing

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

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

3 个答案:

答案 0 :(得分: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; }
}

答案 1 :(得分:0)

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

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

似乎没有其他选择,除非你可以在这里审查你的设计。如果课程很难测试,那么这可能是设计不佳的标志。

答案 2 :(得分:0)

当我不得不在测试中创建复杂的对象时,我使用了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,这些数据在过去对我有用。

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

相关问题