为什么 Matching.ImplementedInterfaces 的行为不同于 Matching.ExactType 和 FrozenAttribute.As?
Why Does the Behavior of Matching.ImplementedInterfaces Differ from Matching.ExactType and FrozenAttribute.As?
请考虑以下代码:
public class TestingSample
{
public class FactoryClass : Class {}
public class Class : IInterface {}
public interface IInterface {}
public class AutoData : AutoDataAttribute
{
public AutoData() : base( Create() ) {}
static IFixture Create()
{
var fixture = new Fixture();
fixture.Customize<IInterface>( composer => composer.FromFactory( () => new FactoryClass() ) );
fixture.Customize<Class>( composer => composer.FromFactory( () => new FactoryClass() ) );
return fixture;
}
}
[Theory, TestingSample.AutoData]
public void OldSkool( [Frozen( As = typeof(IInterface) )]Class first, Class second, IInterface third )
{
Assert.IsType<FactoryClass>( first );
Assert.Same( first, second );
Assert.Same( first, third );
}
[Theory, TestingSample.AutoData]
public void DirectBaseType( [Frozen( Matching.ExactType )]Class first, Class second )
{
Assert.IsType<FactoryClass>( first );
Assert.Same( first, second );
}
[Theory, TestingSample.AutoData]
public void ImplementedInterfaces( [Frozen( Matching.ImplementedInterfaces )]Class first, IInterface second )
{
Assert.IsType<FactoryClass>( first );
Assert.Same( first, second ); // The Fails.
}
}
如您(希望)所见,ImplementedInterfaces
测试失败。由于 FrozenAttribute.As
已被弃用并且用户已被引导到 Match 枚举,我的期望是它的行为与以前相同。
但是,Match.ImplementedInterfaces
的行为似乎与 Match.ExactType
和 FrozenAttribute.As
不同。
我确实做了一些探索,发现 Match.ExactType
和 FrozenAttribute.As
使用 SeedRequestSpecification
而 Match.ImplementedInterfaces
只匹配 Type
请求。
是否可以获得有关此行为的一些背景信息?这是设计使然吗?如果是这样,是否有已知的建议以这种方式进行设计以使用 Match.ImplementedInterfaces
恢复旧行为?
首先,一个附带条件:在我的机器上,使用 AutoFixture 3.39.0,OP 中提供的代码并不像描述的那样相当。不同的是,这个测试中的第一个断言通过了:
[Theory, TestingSample.AutoData]
public void ImplementedInterfaces(
[Frozen(Matching.ImplementedInterfaces)]Class first,
IInterface second)
{
Assert.IsType<FactoryClass>(first); // passes
Assert.Same(first, second); // fails
}
不过,我承认第二个断言失败(有点)令人惊讶。
简短的解释是,在当前的实施中,冻结是在 reflection-time 上完成的,而不是在 run-time 上完成的。当 AutoFixture.Xunit2 确定要冻结的内容时,它会查看应用了 [Frozen]
属性的参数类型。这是 Class
,而不是 FactoryClass
,所以结果是 FactoryClass
根本没有被冻结!
你可以从这个测试中看出这一点:
[Theory, TestingSample.AutoData]
public void FactoryClassIsNotFrozen(
[Frozen(Matching.ImplementedInterfaces)]Class first,
FactoryClass second)
{
Assert.IsType<FactoryClass>(first); // passes
Assert.IsType<FactoryClass>(second); // passes
Assert.Same(first, second); // fails
}
这是最好的实现方式吗?也许不是,但这就是它目前的工作方式。 an open issue in the AutoFixture GitHub repository 建议重构冻结实现,使其更像 DI 容器的单例生命周期。这可能会将此特定场景中的行为改变为更适合它的行为。它是否也会有一些缺点我现在还不能说。
当我们重新设计 [Frozen]
属性以使用更灵活的 Matching
规则时,我意识到新系统无法 100% 替代旧 As
属性。我仍然认为 trade-off 值得。
虽然 As
使您能够使用此特定功能,但这是因为作为程序员,您 知道 Class
实现了 IInterface
,因此 [Frozen(As = typeof(IInterface))]
注释是有意义的。
你可能会说 As
更灵活,但这主要是因为它没有 built-in 智能。您也可以编写 [Frozen(As = typeof(IAsyncResult))]
并且编译得很好 - 只是在 run-time 处失败,因为它完全是胡说八道。
is there a known recommendation to design in such a way to restore the old behavior using Match.ImplementedInterfaces?
是,考虑简化被测系统 (SUT) 的设计。
AutoFixture 最初被设想为 Test-Driven 开发工具,这仍然是它的主要目的。本着 GOOS 的精神,我们应该 听测试 。如果测试难写,第一反应应该是简化SUT。 AutoFixture 倾向于放大来自测试的此类反馈。
您真的需要匹配既实现接口又派生自基础的东西吗class?为什么?
能再简单点吗?
请考虑以下代码:
public class TestingSample
{
public class FactoryClass : Class {}
public class Class : IInterface {}
public interface IInterface {}
public class AutoData : AutoDataAttribute
{
public AutoData() : base( Create() ) {}
static IFixture Create()
{
var fixture = new Fixture();
fixture.Customize<IInterface>( composer => composer.FromFactory( () => new FactoryClass() ) );
fixture.Customize<Class>( composer => composer.FromFactory( () => new FactoryClass() ) );
return fixture;
}
}
[Theory, TestingSample.AutoData]
public void OldSkool( [Frozen( As = typeof(IInterface) )]Class first, Class second, IInterface third )
{
Assert.IsType<FactoryClass>( first );
Assert.Same( first, second );
Assert.Same( first, third );
}
[Theory, TestingSample.AutoData]
public void DirectBaseType( [Frozen( Matching.ExactType )]Class first, Class second )
{
Assert.IsType<FactoryClass>( first );
Assert.Same( first, second );
}
[Theory, TestingSample.AutoData]
public void ImplementedInterfaces( [Frozen( Matching.ImplementedInterfaces )]Class first, IInterface second )
{
Assert.IsType<FactoryClass>( first );
Assert.Same( first, second ); // The Fails.
}
}
如您(希望)所见,ImplementedInterfaces
测试失败。由于 FrozenAttribute.As
已被弃用并且用户已被引导到 Match 枚举,我的期望是它的行为与以前相同。
但是,Match.ImplementedInterfaces
的行为似乎与 Match.ExactType
和 FrozenAttribute.As
不同。
我确实做了一些探索,发现 Match.ExactType
和 FrozenAttribute.As
使用 SeedRequestSpecification
而 Match.ImplementedInterfaces
只匹配 Type
请求。
是否可以获得有关此行为的一些背景信息?这是设计使然吗?如果是这样,是否有已知的建议以这种方式进行设计以使用 Match.ImplementedInterfaces
恢复旧行为?
首先,一个附带条件:在我的机器上,使用 AutoFixture 3.39.0,OP 中提供的代码并不像描述的那样相当。不同的是,这个测试中的第一个断言通过了:
[Theory, TestingSample.AutoData]
public void ImplementedInterfaces(
[Frozen(Matching.ImplementedInterfaces)]Class first,
IInterface second)
{
Assert.IsType<FactoryClass>(first); // passes
Assert.Same(first, second); // fails
}
不过,我承认第二个断言失败(有点)令人惊讶。
简短的解释是,在当前的实施中,冻结是在 reflection-time 上完成的,而不是在 run-time 上完成的。当 AutoFixture.Xunit2 确定要冻结的内容时,它会查看应用了 [Frozen]
属性的参数类型。这是 Class
,而不是 FactoryClass
,所以结果是 FactoryClass
根本没有被冻结!
你可以从这个测试中看出这一点:
[Theory, TestingSample.AutoData]
public void FactoryClassIsNotFrozen(
[Frozen(Matching.ImplementedInterfaces)]Class first,
FactoryClass second)
{
Assert.IsType<FactoryClass>(first); // passes
Assert.IsType<FactoryClass>(second); // passes
Assert.Same(first, second); // fails
}
这是最好的实现方式吗?也许不是,但这就是它目前的工作方式。 an open issue in the AutoFixture GitHub repository 建议重构冻结实现,使其更像 DI 容器的单例生命周期。这可能会将此特定场景中的行为改变为更适合它的行为。它是否也会有一些缺点我现在还不能说。
当我们重新设计 [Frozen]
属性以使用更灵活的 Matching
规则时,我意识到新系统无法 100% 替代旧 As
属性。我仍然认为 trade-off 值得。
虽然 As
使您能够使用此特定功能,但这是因为作为程序员,您 知道 Class
实现了 IInterface
,因此 [Frozen(As = typeof(IInterface))]
注释是有意义的。
你可能会说 As
更灵活,但这主要是因为它没有 built-in 智能。您也可以编写 [Frozen(As = typeof(IAsyncResult))]
并且编译得很好 - 只是在 run-time 处失败,因为它完全是胡说八道。
is there a known recommendation to design in such a way to restore the old behavior using Match.ImplementedInterfaces?
是,考虑简化被测系统 (SUT) 的设计。
AutoFixture 最初被设想为 Test-Driven 开发工具,这仍然是它的主要目的。本着 GOOS 的精神,我们应该 听测试 。如果测试难写,第一反应应该是简化SUT。 AutoFixture 倾向于放大来自测试的此类反馈。
您真的需要匹配既实现接口又派生自基础的东西吗class?为什么?
能再简单点吗?