对于 Autofixture 中的所有实现,省略基础 class 的 属性
Omit property of base class for all implementations in Autofixture
我有 class 的例子如下:
public class BaseItem
{
public Guid Id { get; set; }
public Language Language { get; set; }
}
public class ItemA : BaseItem
{
public string Name { get; set; }
}
public class ItemB : BaseItem
{
public string Path { get; set; }
}
我想在每次创建 ItemA
、ItemB
或 BaseItem
class 的任何未来实现时省略 属性 Language
。
我在自定义 class 中添加了这一行(下方),但在生成 ItemA
/ ItemB
时,AutoFixture 仍会尝试生成 Language
属性.
fixture.Register<BaseItem>(() => { return fixture.Build<BaseItem>().Without(i => i.Language).Create(); });
像这样(见下文)在实现 class 级别上省略 属性 是可行的。
fixture.Register<ItemA>(() => { return fixture.Build<ItemA>().Without(i => i.Language).Create(); });
我的问题是:
- 我如何实现一种通用的方式来做到这一点,这样我就不必自定义每个实现 class?
- 如果有办法执行 #1,如果我希望为某些异常生成语言 属性,我该如何覆盖它?
Q1
为了定位(或者更确切地说,省略)在基础class上定义的属性,我知道没有比定义更好的方法了自定义 ISpecimenBuilder
,在本例中类似于:
public class OmitLanguageBuilder : ISpecimenBuilder
{
public object Create(object request, ISpecimenContext context)
{
var pi = request as PropertyInfo;
if (pi != null &&
pi.PropertyType == typeof(Language)
&& pi.Name == "Language"
&& pi.DeclaringType == typeof(BaseItem))
return new OmitSpecimen();
return new NoSpecimen();
}
}
这专门处理仅在 BaseItem
上声明的 Language
属性 的请求。如果你想不那么具体,你可以删除 if
表达式中的一些谓词。当 AutoFixture 收到这样的请求时,它会将特殊的 'signal type' OmitSpecimen
解释为忽略 属性.
的指令
你可以直接用Fixture
实例注册OmitLanguageBuilder
(虽然你应该将它打包在自定义中。这个测试通过:
[Fact]
public void Q1A()
{
var fixture = new Fixture();
fixture.Customizations.Add(new OmitLanguageBuilder());
var actual = fixture.Create<ItemA>();
Assert.Null(actual.Language);
Assert.NotEqual(default(Guid), actual.Id);
Assert.NotNull(actual.Name);
}
ItemB
的类似测试也通过了。
Q2
当您想在特殊测试用例中填充 Language
时,最简单的解决方案可能只是在事后填充它:
[Fact]
public void Q2SetAfter()
{
var fixture = new Fixture();
fixture.Customizations.Add(new OmitLanguageBuilder());
var actual = fixture.Create<ItemA>();
actual.Language = fixture.Create<Language>();
Assert.NotNull(actual.Language);
Assert.NotEqual(default(Guid), actual.Id);
Assert.NotNull(actual.Name);
}
另一种选择是使用 Build
API:
[Fact]
public void Q2Build()
{
var fixture = new Fixture();
fixture.Customizations.Add(new OmitLanguageBuilder());
var actual =
fixture.Build<ItemA>().With(x => x.Language, fixture.Create<Language>()).Create();
Assert.NotNull(actual.Language);
Assert.NotEqual(default(Guid), actual.Id);
Assert.NotNull(actual.Name);
}
最后,如果您需要更改 AutoFixture 处理类型的一般方式,您可以这样做:
[Fact]
public void Q2Customize()
{
var fixture = new Fixture();
fixture.Customizations.Add(new OmitLanguageBuilder());
fixture.Customize<ItemA>(c => c.With(x => x.Language, fixture.Create<Language>()));
var actual = fixture.Create<ItemA>();
Assert.NotNull(actual.Language);
Assert.NotEqual(default(Guid), actual.Id);
Assert.NotNull(actual.Name);
}
我有 class 的例子如下:
public class BaseItem
{
public Guid Id { get; set; }
public Language Language { get; set; }
}
public class ItemA : BaseItem
{
public string Name { get; set; }
}
public class ItemB : BaseItem
{
public string Path { get; set; }
}
我想在每次创建 ItemA
、ItemB
或 BaseItem
class 的任何未来实现时省略 属性 Language
。
我在自定义 class 中添加了这一行(下方),但在生成 ItemA
/ ItemB
时,AutoFixture 仍会尝试生成 Language
属性.
fixture.Register<BaseItem>(() => { return fixture.Build<BaseItem>().Without(i => i.Language).Create(); });
像这样(见下文)在实现 class 级别上省略 属性 是可行的。
fixture.Register<ItemA>(() => { return fixture.Build<ItemA>().Without(i => i.Language).Create(); });
我的问题是:
- 我如何实现一种通用的方式来做到这一点,这样我就不必自定义每个实现 class?
- 如果有办法执行 #1,如果我希望为某些异常生成语言 属性,我该如何覆盖它?
Q1
为了定位(或者更确切地说,省略)在基础class上定义的属性,我知道没有比定义更好的方法了自定义 ISpecimenBuilder
,在本例中类似于:
public class OmitLanguageBuilder : ISpecimenBuilder
{
public object Create(object request, ISpecimenContext context)
{
var pi = request as PropertyInfo;
if (pi != null &&
pi.PropertyType == typeof(Language)
&& pi.Name == "Language"
&& pi.DeclaringType == typeof(BaseItem))
return new OmitSpecimen();
return new NoSpecimen();
}
}
这专门处理仅在 BaseItem
上声明的 Language
属性 的请求。如果你想不那么具体,你可以删除 if
表达式中的一些谓词。当 AutoFixture 收到这样的请求时,它会将特殊的 'signal type' OmitSpecimen
解释为忽略 属性.
你可以直接用Fixture
实例注册OmitLanguageBuilder
(虽然你应该将它打包在自定义中。这个测试通过:
[Fact]
public void Q1A()
{
var fixture = new Fixture();
fixture.Customizations.Add(new OmitLanguageBuilder());
var actual = fixture.Create<ItemA>();
Assert.Null(actual.Language);
Assert.NotEqual(default(Guid), actual.Id);
Assert.NotNull(actual.Name);
}
ItemB
的类似测试也通过了。
Q2
当您想在特殊测试用例中填充 Language
时,最简单的解决方案可能只是在事后填充它:
[Fact]
public void Q2SetAfter()
{
var fixture = new Fixture();
fixture.Customizations.Add(new OmitLanguageBuilder());
var actual = fixture.Create<ItemA>();
actual.Language = fixture.Create<Language>();
Assert.NotNull(actual.Language);
Assert.NotEqual(default(Guid), actual.Id);
Assert.NotNull(actual.Name);
}
另一种选择是使用 Build
API:
[Fact]
public void Q2Build()
{
var fixture = new Fixture();
fixture.Customizations.Add(new OmitLanguageBuilder());
var actual =
fixture.Build<ItemA>().With(x => x.Language, fixture.Create<Language>()).Create();
Assert.NotNull(actual.Language);
Assert.NotEqual(default(Guid), actual.Id);
Assert.NotNull(actual.Name);
}
最后,如果您需要更改 AutoFixture 处理类型的一般方式,您可以这样做:
[Fact]
public void Q2Customize()
{
var fixture = new Fixture();
fixture.Customizations.Add(new OmitLanguageBuilder());
fixture.Customize<ItemA>(c => c.With(x => x.Language, fixture.Create<Language>()));
var actual = fixture.Create<ItemA>();
Assert.NotNull(actual.Language);
Assert.NotEqual(default(Guid), actual.Id);
Assert.NotNull(actual.Name);
}