我希望自定义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<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 :(得分:6)
对于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<MyClass>
将要抛出,因为它将尝试将“bar”以外的东西分配给该属性。
在一次性场景中,可以使用Build
方法来解决此问题。一个例子就是将值明确设置为“bar”:
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));
现在,每当fixture创建一个MyClass实例时,它就会完全跳过该属性。您仍然可以在之后分配值:
var mc = fixture.CreateAnonymous<MyClass>();
my.SomeWeirdText = "bar";
如果你想要更复杂的东西,你可以implement a custom ISpecimenBuilder。如果要在创建实例后运行一些自定义代码,可以使用后处理器装饰自己的ISpecimenBuilder并提供委托。这可能看起来像这样:
fixture.Customizations.Add(
new Postprocessor(yourCustomSpecimenBuilder, obj =>
{ */ do something to obj here */ }));
(顺便说一下,你还在使用AutoFixture 1.0吗?IIRC,从那时起就没有ObjectBuilder<T>
......)
答案 1 :(得分: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