c# - 为什么 Matching.ImplementedInterfaces 的行为与 Matching.ExactType 和 FrozenAttribute.As 不同?

标签 c# autofixture

请考虑以下代码:

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.ExactTypeFrozenAttribute.As 不同。

我确实做了一些探索,发现 Match.ExactTypeFrozenAttribute.As 使用了 SeedRequestSpecificationMatch. 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
}

不过,我承认第二个断言失败是(有点)令人惊讶的。

简单的解释是,在当前的实现中,卡住是在反射时完成的,而不是在运行时完成的。当 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建议应该重构 freeze 实现,使其更像 DI 容器的 Singleton 生命周期。这可能会将这种特定场景中的行为改变为更适合它的行为。它是否也会有一些缺点,目前我还无法判断。

当我们重新设计[Frozen]属性以使用更灵活的Matching规则时,我意识到新系统无法100%替换旧的 As 属性。我仍然认为这种权衡是值得的。

虽然 As 使您能够让此特定功能发挥作用,但这是因为您作为程序员知道 Class 实现了 IInterface,因此 [Frozen(As = typeof(IInterface))] 注释是有意义的。

您可能会说 As 更灵活,但这主要是因为它没有内置智能。您也可以编写 [Frozen(As = typeof(IAsyncResult))] 并且可以很好地编译 - 只是在运行时失败,因为它完全是无意义的。

is there a known recommendation to design in such a way to restore the old behavior using Match.ImplementedInterfaces?

是的,考虑简化被测系统 (SUT) 的设计。

AutoFixture 最初被设想为测试驱动开发工具,这仍然是其主要目的。本着GOOS的精神,我们应该听测试。如果测试很难编写,第一 react 应该是简化 SUT。 AutoFixture 往往会放大来自测试的此类反馈。

您真的需要匹配既实现接口(interface)又派生自基类的东西吗?为什么?

能不能再简单一点?

关于c# - 为什么 Matching.ImplementedInterfaces 的行为与 Matching.ExactType 和 FrozenAttribute.As 不同?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34840015/

相关文章:

c# - 如何使用带有 FakeItEasy 的 AutoFixture 注册未明确定义为 Strict 的依赖项?

c# - NSubstitute 和 AutoFixture 与 Received 相关的问题

c# - 如何防止 AutoFixture 的 GuardClauseAssertion 检查继承类中的构造函数?

c# - XDocument 到 JSON,JsonProperties

c# - 如何为 Outlook 添加身份验证到 ASP.NET 托管的 ICS iCalendar

c# - 表单例份验证不适用于特定页面

c# - 当等待模拟方法时,单元测试中的 NRE

c# - 如何将基于约定的自定义与 AutoFixture 的 [AutoData] 属性相结合?

c# - Moq中的设置方法,调用不明确

c# - 实例变量