让我首先说我是这种模式优雅的忠实粉丝 - 我有一组基本实体,我已经实现了构建器(专门用于测试)。然而,我发现(这可能是警告)随着我的程序的发展,我不得不回去重建工程师。最后,让它们保持更新似乎并不值得,我回过头来主要保留一个具有大量预配置实体的Object Mother。我是否应该继续更新构建器以供将来使用,或者TDB是否只应在设计达到某种稳定性并且Object Mother变得过大时才能创建?
另请注意,我发现我没有在应用程序的其他任何地方使用构建器,因为我喜欢使用.Net 3.0的新属性初始化语法。
答案 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);
}
}