无法掌握Freeze/Inject/Register之间的区别

Joh*_*ohn 43 c# autofixture

在开始之前,我是AutoFixture的忠实粉丝,我仍然在学习如何使用该工具.所以感谢开发了Autofixture先生Ploeh和所有贡献者.

那么让我们从我的问题开始吧.

根据 AutoFixture/AutoMoq忽略注入实例/冻结模拟

上面链接的有趣部分是给出了这段代码

Mock<ISettings> settingsMock = new Mock<ISettings>();
settingsMock.Setup(s => s.Get(settingKey)).Returns(xmlString);

ISettings settings = settingsMock.Object;
fixture.Inject(settings);
Run Code Online (Sandbox Code Playgroud)

马克回答哪个可以重写

fixture.Freeze<Mock<ISettings>>()
       .Setup(s => s.Get(settingKey)).Returns(xmlString);
Run Code Online (Sandbox Code Playgroud)

它看起来像一个语法糖,使用Freeze方法是一种在流畅的界面中编写模拟,配置和自动混合容器中的注入的方法.

在对网络进行一些研究之后,Freeze和Inject之间实际上存在功能差异.我发现了这个问题:https: //github.com/AutoFixture/AutoFixture/issues/59 ,它指出了 如何在AutoFixture中冻结空实例的答案

上面链接的作者描述了Freeze方法如下:

在内部,Freeze创建一个所请求类型的实例(例如IPayPalConfiguration)然后注入它,这样当你再次请求它时它将始终返回该实例

我明白我们这样做的时候

var customer = fixture.Freeze<Order>();
Run Code Online (Sandbox Code Playgroud)

每当我们的代码请求Order类型时,它将始终使用相同的Order实例.但是如果我在Freeze构造函数中指定我希望它使用特定的实例呢?

这是一个小代码示例:

[Fact]
public void MethodeName()
{
    var fixture = new Fixture().Customize(new AutoMoqCustomization());
    fixture.Freeze<OrderLine>(new OrderLine("Foo"));
    var order = fixture.Create<Order>();
}

public class Order
{
    private readonly OrderLine _line;

    public Order(OrderLine line)
    {
        _line = line;
    }
}
public class OrderLine
{
    private readonly string _name;

    public OrderLine(string name)
    {
        _name = name;
    }
}
Run Code Online (Sandbox Code Playgroud)

OrderLine的名称不应该等于"Foo"而不是namefe48163a-d5a0-49a5-b349-7b11ba5f804b吗?Freeze方法的文档说:

<typeparam name="T">The type to freeze.</typeparam>
<param name="fixture">The fixture.</param>
<param name="seed">Any data that adds additional information when creating the anonymous object. Hypothetically, this value might be the value being frozen, but this is not likely.</param>
Run Code Online (Sandbox Code Playgroud)

为什么作者不确定何时返回该值?如果我在Freeze的构造函数中指定我的实例,我期望自动混合使用此实例?

然后

请注意,除非您已自定义执行此操作,否则不可能将其用作冻结值.如果您希望将特定值注入Fixture,则应使用该方法

好像我必须自定义种子参数.任何人都可以澄清吗?文档指出的解决方案是使用Inject方法.事实上,它适用于我的OrderLine代码示例.

我正在寻找你的帮助,以了解Freeze,Inject和Register之间的区别,根据源代码,它只是由Inject方法调用,但它需要一个lambda.

Mar*_*ann 44

注册和注入

曾几何时,没有Inject,没有Freeze; Register统治代码.

那时候,有一个Register过载定义:

public static void Register<T>(this IFixture fixture, T item)
Run Code Online (Sandbox Code Playgroud)

但是,它必须与这个近亲共享API:

public static void Register<T>(this IFixture fixture, Func<T> creator)
Run Code Online (Sandbox Code Playgroud)

AutoFixture的创造者认为这很好,但唉:用户感到困惑.最悲惨的是,用户可以写:

fixture.Register(() => universe.LightUp());
Run Code Online (Sandbox Code Playgroud)

但是也

fixture.Register(universe.LightUp);
Run Code Online (Sandbox Code Playgroud)

这意味着完全相同的事情,因为universe.LightUp是对方法的引用,因此匹配委托.

但是,该语法看起来像属性引用,因此如果LightUp是属性而不是方法,则编译器将选择第一个重载.

这引起了很多混乱,因此Register<T>(this IFixture fixture, T item)重载被重命名为Inject<T>(this IFixture fixture, T item).

冻结

冻结有不同的历史.很久以前,当我仍然以强制方式使用AutoFixture时,我注意到我反复编写这样的代码:

var foo = fixture.Create<Foo>();
fixture.Inject(foo);
Run Code Online (Sandbox Code Playgroud)

所以我认为这是一个概念并命名为Freeze.该Freeze方法只是这两行代码的简写.

我正在寻找你的帮助,以了解Freeze,Inject和Register之间的区别,根据源代码,它只是由Inject方法调用,但它需要一个lambda

一般来说,区分Inject和之间不应该太难Register,因为它们的签名不会发生碰撞.因此,如果您尝试使用这两种方法中的一种来完成目标,并且您的代码已编译,那么您可能选择了正确的版本.

Freeze如果不是OP中使用的过载,也会出现这种情况:

[EditorBrowsable(EditorBrowsableState.Never)]
public static T Freeze<T>(this IFixture fixture, T seed)
Run Code Online (Sandbox Code Playgroud)

请注意,这种重载确实存在EditorBrowsableState.Never,因为它总是让人感到困惑.然而,尽管如此,显然人们仍然发现超载,所以我认为它应该在AutoFixture 4中移动.它是存在的功能之一,因为它易于实现......

  • 顺便说一句.仍然发现此重载的一个原因可能是ReSharper似乎默认设置为忽略`EditorBrowsable`属性. (6认同)

Nik*_*nis 14

Freeze,InjectRegister所有都是自定义创建算法.

使用InjectRegister明确指定应以特定方式创建对象,在您的示例中通过new OrderLine("Foo")手动提供.

随着Freeze你没有指定如何对象应创建-你问AutoFixture为您提供一个实例.

最后,所有上述方法都使用相同的低级API:

fixture.Customize<T>(c => c.FromFactory(creator).OmitAutoProperties());


之所以fixture.Freeze<OrderLine>(new OrderLine("Foo"));不创建OrderLine具有指定种子值的实例,是因为默认情况下会忽略种子.

要支持特定类型的种子值,您可以创建SeedFavoringRelay<T>:

public class SeedFavoringRelay<T> : ISpecimenBuilder where T : class
{
    public object Create(object request, ISpecimenContext context)
    {
        if (context == null)
            throw new ArgumentNullException("context");

        var seededRequest = request as SeededRequest;
        if (seededRequest == null || !seededRequest.Request.Equals(typeof(T)))
            return new NoSpecimen(request);

        var seed = seededRequest.Seed as T;
        if (seed == null)
            return new NoSpecimen(request);

        return seed;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后你可以使用它如下:

fixture.Customizations.Add(
    new SeedFavoringRelay<OrderLine>());

fixture.Freeze<OrderLine>(new OrderLine("Foo"));
// -> Now fixture.Create<Order>() creates an Order with OrderLine's Name = "Foo".
Run Code Online (Sandbox Code Playgroud)