AutoFixture可以在对象创建时执行委托吗?

我正在寻找自定义AutoFixture的创建时行为,这样我可以在生成和分配夹具的属性后设置一些依赖对象。

例如,假设我有一个自定义User的方法,因为对于某组测试,其IsDeleted属性始终必须为false:

 public class User { public int Id { get; set; } public string Name { get; set; } public bool IsDeleted { get; set; } } public static ObjectBuilder BuildUser(this Fixture f) { return f.Build().With(u => u.IsDeleted, false); } 

(我将ObjectBuilder交回测试,以便在必要时可以进一步自定义夹具。)

我想要做的是在创建时自动将该用户与其匿名集合关联,但我不能这样做,因为当我将返回值返回给用户时,我还没有生成Idunit testing正确。 这是我正在尝试做的事情:

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

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

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

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

即想象一个这样的类:

 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将要抛出,因为它将尝试为该属性分配除“bar”之外的其他内容。

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

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

但是,更简单的是省略该属性:

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

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

首先,可以将遗漏属性转换为自定义,如下所示:

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

现在,每当fixture创建一个MyClass实例时,它就会完全跳过该属性。 您仍然可以在之后分配值:

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

如果您想要更复杂的东西,可以实现自定义的ISpecimenBuilder 。 如果要在创建实例后运行一些自定义代码,可以使用后处理器装饰自己的ISpecimenBuilder并提供委托。 这可能看起来像这样:

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

(顺便说一句,你还在使用AutoFixture 1.0吗?IIRC,从那时起就没有一个ObjectBuilder …)

关于AutoFixture CodePlex网站上有关此主题的有用讨论 。

我相信我的后期处理器定制链接应该可以帮到你。 用法示例:

 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 { public ApplyControllerContextCustomization() : base( PostProcess ) { } static void PostProcess( Controller controller ) { controller.FakeControllerContext(); // etc. - add stuff you want to happen after the instance has been created