测试数据生成器模式:更有用还是更保养?

时间:2008-10-09 15:25:23

标签: .net unit-testing design-patterns

让我首先说我是这种模式优雅的忠实粉丝 - 我有一组基本实体,我已经实现了构建器(专门用于测试)。然而,我发现(这可能是警告)随着我的程序的发展,我不得不回去重建工程师。最后,让它们保持更新似乎并不值得,我回过头来主要保留一个具有大量预配置实体的Object Mother。我是否应该继续更新构建器以供将来使用,或者TDB是否只应在设计达到某种稳定性并且Object Mother变得过大时才能创建?

另请注意,我发现我没有在应用程序的其他任何地方使用构建器,因为我喜欢使用.Net 3.0的新属性初始化语法。

3 个答案:

答案 0 :(得分:8)

我喜欢使用流畅的构建器来测试被测对象,以表达我正在创建的对象的性质。 ObjectMothers倾向于变得笨拙并倾向于(在我遇到的实现中)最终隐藏对象创建的细节。

比较

User fred = CreateUser("fred").WithReputation(900)
                              .WithScholarBadge()
                              .WithCriticBadge()

VS

User fred = UserObjectMother.Fred()

表达用户拥有rep 900并且这两个特定徽章与ObjectMother不相符的想法。我看到的这种趋势是开发人员然后找到构建Fred()的方法,这种方法接近他们需要的方式,因此他们为对象添加了更多属性。另一方面,流利的构建器表达了正在构建的内容,并且可以根据需要轻松地为测试创建特定用户。

尽管如此,我最终还是只在测试代码中使用这些模式,因为生产代码通常不需要这种表达方式。

答案 1 :(得分:1)

如果测试数据构建器变得太复杂而无法维护,我建议切换到模拟框架,这样可以更方便地生成具有已知状态的域测试对象。登记/> 此外,使用模拟,您可以使测试对象不仅仅是简单的存根,并且通过设置期望如何调用其属性和方法,实际参与测试所声称的内容。

答案 2 :(得分:0)

Lambda语法可以使流畅的界面更简洁

例如,

User fred = new UserTestDataBuilder()
                .With(u => u.Name = "fred")
                .With(u => u.Reputation = 900)
                .With(u => u.ScholarBadge = true)
                .With(u => u.CriticBadge = true)

您只需要一个Action方法和一个用于填充属性的类。

public class UserSpec
{
    public string Name {get; set;}
    public int Reputation {get; set;}
    ...
}

public class UserTestDataBuilder() 
{
    private UserSpec _userSpec = new UserSpec();
    public UserTestDataBuilder With(Action<UserSpec> action) 
    {
        action(_userSpec);
        return this;
    }

    public User Build() 
    {
        return new User(_userSpec.Name, _userSpec.Reputation, _userSpec.ScholarBadge, _userSpec.CriticBadge);
    }
}