0

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

3 回答 3

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; }
}
于 2013-07-25T20:44:26.667 回答
0

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

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

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

于 2013-07-25T20:55:19.673 回答
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,这些库过去对我来说效果很好。

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

于 2013-07-25T21:12:07.833 回答