如何使用 AutoFixture 注册装饰器?
How do I register decorators with AutoFixture?
Decorator pattern demonstrates how we can extend the behaviour of a component without modifying the underlying implementation. But this means I have two components that implement the same interface. Is there a way to Register
these in AutoFixture 这样我仍然可以多态地引用我的组件作为它的接口?
一个代码示例帮助我理解了我的意思。假设我想用 LoggingComponent
装饰 IComponent
interface IComponent
{
void DoStuff();
}
class Component : IComponent
{
public void DoStuff()
{
// Do something amazing!
}
}
class LoggingComponent : IComponent
{
private readonly IComponent _Component;
public LoggingComponent(IComponent Component)
{
_Component = Component;
}
public void DoStuff()
{
Console.WriteLine("Calling DoStuff()");
_Component.DoStuff();
}
}
我如何将 LoggingComponent
注册为 IComponent
并创建没有循环依赖的 LoggingComponent
?
用这种方式注册 LoggingComponent
还有什么选择:
_fixture.Register<IComponent>(_fixture.Create<LoggingComponent>);
然后尝试使用以下代码创建 IComponent
的实例:
var Component = _fixture.Create<IComponent>();
这显然会导致抛出此 ObjectCreationException 异常:
AutoFixture was unable to create an instance of type Ploeh.AutoFixture.Kernel.SeededRequest because the traversed object graph contains a circular reference. Information about the circular path follows below. This is the correct behavior when a Fixture is equipped with a ThrowingRecursionBehavior, which is the default. This ensures that you are being made aware of circular references in your code. Your first reaction should be to redesign your API in order to get rid of all circular references. However, if this is not possible (most likely because parts or all of the API is delivered by a third party), you can replace this default behavior with a different behavior: on the Fixture instance, remove the ThrowingRecursionBehavior from Fixture.Behaviors, and instead add an instance of OmitOnRecursionBehavior.
您需要先冻结 IComponent
。
然后,AutoFixture 将在创建 LoggingDecorator
class.
时重新使用相同的 frozen IComponent
实例
这里有两种方法:
通过自动模拟使用 AutoFixture:
PM> Install-Package AutoFixture.AutoMoq
[Fact]
public void UsingAutoFixtureAutoMoq()
{
var fixture = new Fixture()
.Customize(new AutoMoqCustomization());
var expected = fixture.Freeze<Mock<IComponent>>().Object;
var decorator = fixture.Create<LoggingComponent>();
var actual = decorator.Component;
Assert.Equal(expected, actual);
}
将 AutoFixture 与 Auto Mocking 和 xUnit.net 数据理论结合使用:
PM> Install-Package AutoFixture.Xunit
PM> Install-Package AutoFixture.AutoMoq
[Theory, TestConventions]
public void UsingAutoFixtureAutoMoqWithXunitTheories(
[Frozen]Mock<IComponent> inner,
LoggingComponent decorator)
{
var expected = inner.Object;
var actual = decorator.Component;
Assert.Equal(expected, actual);
}
TestConventions
属性 class 定义为:
internal class TestConventionsAttribute : AutoDataAttribute
{
internal TestConventionsAttribute()
: base(
new Fixture().Customize(
new AutoMoqCustomization()))
{
}
}
注意:要编译和运行上述测试,为IComponent
添加一个Inspection Property到LoggingDecorator
.
Decorator pattern demonstrates how we can extend the behaviour of a component without modifying the underlying implementation. But this means I have two components that implement the same interface. Is there a way to Register
these in AutoFixture 这样我仍然可以多态地引用我的组件作为它的接口?
一个代码示例帮助我理解了我的意思。假设我想用 LoggingComponent
IComponent
interface IComponent
{
void DoStuff();
}
class Component : IComponent
{
public void DoStuff()
{
// Do something amazing!
}
}
class LoggingComponent : IComponent
{
private readonly IComponent _Component;
public LoggingComponent(IComponent Component)
{
_Component = Component;
}
public void DoStuff()
{
Console.WriteLine("Calling DoStuff()");
_Component.DoStuff();
}
}
我如何将 LoggingComponent
注册为 IComponent
并创建没有循环依赖的 LoggingComponent
?
用这种方式注册 LoggingComponent
还有什么选择:
_fixture.Register<IComponent>(_fixture.Create<LoggingComponent>);
然后尝试使用以下代码创建 IComponent
的实例:
var Component = _fixture.Create<IComponent>();
这显然会导致抛出此 ObjectCreationException 异常:
AutoFixture was unable to create an instance of type Ploeh.AutoFixture.Kernel.SeededRequest because the traversed object graph contains a circular reference. Information about the circular path follows below. This is the correct behavior when a Fixture is equipped with a ThrowingRecursionBehavior, which is the default. This ensures that you are being made aware of circular references in your code. Your first reaction should be to redesign your API in order to get rid of all circular references. However, if this is not possible (most likely because parts or all of the API is delivered by a third party), you can replace this default behavior with a different behavior: on the Fixture instance, remove the ThrowingRecursionBehavior from Fixture.Behaviors, and instead add an instance of OmitOnRecursionBehavior.
您需要先冻结 IComponent
。
然后,AutoFixture 将在创建 LoggingDecorator
class.
IComponent
实例
这里有两种方法:
通过自动模拟使用 AutoFixture:
PM> Install-Package AutoFixture.AutoMoq
[Fact]
public void UsingAutoFixtureAutoMoq()
{
var fixture = new Fixture()
.Customize(new AutoMoqCustomization());
var expected = fixture.Freeze<Mock<IComponent>>().Object;
var decorator = fixture.Create<LoggingComponent>();
var actual = decorator.Component;
Assert.Equal(expected, actual);
}
将 AutoFixture 与 Auto Mocking 和 xUnit.net 数据理论结合使用:
PM> Install-Package AutoFixture.Xunit
PM> Install-Package AutoFixture.AutoMoq
[Theory, TestConventions]
public void UsingAutoFixtureAutoMoqWithXunitTheories(
[Frozen]Mock<IComponent> inner,
LoggingComponent decorator)
{
var expected = inner.Object;
var actual = decorator.Component;
Assert.Equal(expected, actual);
}
TestConventions
属性 class 定义为:
internal class TestConventionsAttribute : AutoDataAttribute
{
internal TestConventionsAttribute()
: base(
new Fixture().Customize(
new AutoMoqCustomization()))
{
}
}
注意:要编译和运行上述测试,为IComponent
添加一个Inspection Property到LoggingDecorator
.