2012-04-05 40 views
6

我期待定制AutoFixture的创建时行为,以便在生成并分配灯具的属性后,我可以设置一些相关对象。AutoFixture可以在创建对象时执行委托吗?

例如,假设我有一个自定义的方法的User,因为它的IsDeleted属性总是有成为一组特定的测试失败:

public class User 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
    public bool IsDeleted { get; set; } 
} 

public static ObjectBuilder<User> BuildUser(this Fixture f) 
{ 
    return f.Build<User>().With(u => u.IsDeleted, false); 
} 

(我手的ObjectBuilder回测试,因此如果需要,可以进一步定制夹具。)

我想在创建时自动将该用户与其匿名集合Id关联起来,但我不能这样做,因为Id还没有由我手中产生的时间返回值返回到单元测试的适当位置。这是我想要做的事情:

public static ObjectBuilder<User> BuildUserIn(this Fixture f, UserCollection uc) 
{ 
    return f.Build<User>() 
      .With(u => u.IsDeleted, false); 
      .AfterCreation(u => 
      { 
       var relation = f.Build<UserCollectionMembership>() 
           .With(ucm => ucm.UserCollectionId, uc.Id) 
           .With(ucm => ucm.UserId, u.Id) 
           .CreateAnonymous(); 
       Repository.Install(relation); 
      } 
} 

是这样的可能吗?或者有更好的方法来实现我创建匿名对象图的目标?

+0

你想为用户类型的特定实例,并重新使用它的ID属性值别的地方? – 2012-04-05 19:17:35

+2

这有帮助吗? http://stackoverflow.com/questions/5398258/customizing-autofixture-builder-with-seeded-property/5398653#5398653 – 2012-04-05 20:32:26

+0

@MarkSeemann:'Do()'显然可以在对象完全填充之前执行,这样就不会'工作。我可以(也可以)手动执行示例lambda或做我的对象定制后创建,但我喜欢类似于上面的替代! – ladenedge 2012-04-09 07:31:52

回答

6

对于Build方法,这是不可能的,也许永远不会,因为有更好的选择可用。

首先,应该永远不需要围绕Build方法编写静态辅助方法。 Build方法用于在事实之前需要定义属性或字段值的真正的一次性初始化。

I.e.想象一类是这样的:

public class MyClass 
{ 
    private string txt; 

    public string SomeWeirdText 
    { 
     get { return this.txt; } 
     set 
     { 
      if (value != "bar") 
       throw new ArgumentException(); 
      this.txt = value; 
     } 
    } 
} 

在此(做作)例如,直fixture.CreateAnonymous<MyClass>是要扔掉,因为它会尝试给比“巴”的财产以外的东西。

在一次性场景中,可以使用Build方法来解决此问题。一个例子是简单明确的值设置为“栏”:

var mc = 
    fixture.Build<MyClass>().With(x => x.SomeWeirdText, "bar").CreateAnonymous(); 

然而,更容易将只是忽略该属性:

var mc = 
    fixture.Build<MyClass>().Without(x => x.SomeWeirdText).CreateAnonymous(); 

然而,一旦你开始想重复做,有更好的选择。 AutoFixture有一个非常复杂和可定制的引擎,用于定义事物的创建方式。

作为一个开始,一个可以由酒店的遗漏移动到一个定制的,像这样开始:

fixture.Customize<MyClass>(c => c.Without(x => x.SomeWeirdText)); 

现在,每当夹具创建MyClass的实例,它只是要跳过该属性共。您仍然可以在之后分配一个值:

var mc = fixture.CreateAnonymous<MyClass>(); 
my.SomeWeirdText = "bar"; 

如果您想要更复杂一些的东西,您可以implement a custom ISpecimenBuilder。如果您希望在创建实例后运行一些自定义代码,则可以使用Postprocessor修饰您自己的ISpecimenBuilder并提供一个委托。这可能是这个样子:

fixture.Customizations.Add(
    new Postprocessor(yourCustomSpecimenBuilder, obj => 
     { */ do something to obj here */ })); 

(?顺便说一句,你还在上AutoFixture 1.0 IIRC,还没有从那以后是一个ObjectBuilder<T>左右...)

+0

添加一个后处理器可能只是票据,我很欣赏正确的AF使用建议。谢谢! (关于v1.0,是的:我们有一些[麻烦](http://stackoverflow.com/questions/4650424/autofixture-2-with-isnt-working-as-it-did-in-autofixture-1)[升级](http://stackoverflow.com/questions/8595498/why-isnt-autofixture-working-with-the-stringlength-data-annotation) - 也许第三次将是魅力!) – ladenedge 2012-04-12 21:17:28

3

有一个useful discussion on this topic on the AutoFixture CodePlex site

我相信我那里的postprocessor Customization应该可以帮到你。用法示例:

class AutoControllerDataAttribute : AutoDataAttribute 
{ 
    public AutoControllerDataAttribute() 
     : this(new Fixture()) 
    { 
    } 

    public AutoControllerDataAttribute(IFixture fixture) 
     : base(fixture) 
    { 
     fixture.Customize(new AutoMoqCustomization()); 
     fixture.Customize(new ApplyControllerContextCustomization()); 
    } 

    class ApplyControllerContextCustomization : PostProcessWhereIsACustomization<Controller> 
    { 
     public ApplyControllerContextCustomization() 
      : base(PostProcess) 
     { 
     } 

     static void PostProcess(Controller controller) 
     { 
      controller.FakeControllerContext(); 
      // etc. - add stuff you want to happen after the instance has been created 
相关问题