Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can't grasp the difference between Freeze/Inject/Register

Tags:

c#

autofixture

Before starting, I'm a big fan of AutoFixture, I'm still in the curve of learning how to use the tool. So thanks for having developed Autofixture Mr Ploeh and all the contributors.

So let's start with my question.

According to AutoFixture/AutoMoq ignores injected instance/frozen mock

The interesting part of the above link is given this code

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

To which Mark answer it can be rewritten to

fixture.Freeze<Mock<ISettings>>()        .Setup(s => s.Get(settingKey)).Returns(xmlString); 

It looks like a syntaxic sugar, using Freeze method is a way to write in fluent interface the creation of the mock, the configuration, and the injection in autofixture container.

After doing some research on the web, there're actually a functional difference between Freeze and Inject. I found this question: https://github.com/AutoFixture/AutoFixture/issues/59 which point the answer to How can I Freeze a null instance in AutoFixture

The author of the link above describe Freeze method as the following:

Internally, Freeze creates an instance of the requested type (e.g. IPayPalConfiguration) and then injects it so it will always return that instance when you request it again

I understand that when we do

var customer = fixture.Freeze<Order>(); 

it will always use the same instance of Order whenever our code request an Order type. But what if I specify in Freeze constructor that I want it to use a specific instance ?

Here's a little code example:

[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;     } } 

Shouldn't the name of OrderLine be equal to "Foo" instead of namefe48163a-d5a0-49a5-b349-7b11ba5f804b ? The documentation of Freeze method say:

<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> 

why is the author not sure when the value is returned ? If I specify, my instance in the constructor of Freeze, I'm expecting autofixture to use this instance ?

then

Please notice that the isn't likely to be used as the frozen value, unless you've customized to do this. If you wish to inject a specific value into the Fixture, you should use the method instead.`

It seems like I have to customize the seed parameter. Can anyone clarify ? The solution pointed by documentation is to use Inject method. And indeed, it works in my code example with OrderLine.

I'm looking for your help to understand the difference between Freeze, Inject, and also Register which, according to the source code, is just called by Inject method but it takes a lambda.

like image 508
John Avatar asked Aug 10 '13 10:08

John


People also ask

How to freeze an object to make it immutable?

Object.freeze () method: If you want the value of the person object to be immutable, you have to freeze it by using the Object.freeze () method. The Object.freeze () method is shallow, meaning that it can freeze the properties of the object, not the objects referenced by the properties.

What is the difference between object freeze() and const() in JavaScript?

Amongst these new features are Object.freeze () method and const. Sometimes people get confused between Object.freeze () method and const but the Object.freeze () and const are completely different. In this article we will explain the differences between these two. const: The const keyword creates a read-only reference to a value.

Can a refrigerator replace a freezer?

However, a fridge will never be able to act as a replacement for a freezer, while a freezer can be made into a fridge (in a roundabout way). Typically, if you want to run a proper kitchen, then you are going to need to have both a refrigerator and a freezer.


2 Answers

Register and Inject

Once upon a time, there was no Inject and no Freeze; Register ruled the code.

Back then, there was a Register overload defined thusly:

public static void Register<T>(this IFixture fixture, T item) 

However, it had to share the API with this close relative:

public static void Register<T>(this IFixture fixture, Func<T> creator) 

The creator of AutoFixture thought that this was good, but alas: users were stricken with confusion. Most grievously, a user could write:

fixture.Register(() => universe.LightUp()); 

but also

fixture.Register(universe.LightUp); 

which means the exact same thing, because universe.LightUp is a reference to a method, and thus matches a delegate.

However, that syntax looks like a property reference, so if LightUp had been a property instead of a method, the first overload would be selected by the compiler.

This caused much confusion, so the Register<T>(this IFixture fixture, T item) overload was renamed to Inject<T>(this IFixture fixture, T item).

Freeze

Freeze has a different history. A long time ago, when I still used AutoFixture in an imperative way, I noticed that I repeatedly wrote code like this:

var foo = fixture.Create<Foo>(); fixture.Inject(foo); 

So I decided that this was a concept and named it Freeze. The Freeze method is only shorthand for those two lines of code.

I'm looking for your help to understand the difference between Freeze, Inject, and also Register which, according to the source code, is just called by Inject method but it takes a lambda

In general, it shouldn't be too hard to distinguish between Inject and Register, since their signatures don't collide. Thus, if you try to accomplish a goal with one of those two methods, and your code compiles, you probably chose the right version.

This would also be the case for Freeze if it wasn't for the overload used in the OP:

[EditorBrowsable(EditorBrowsableState.Never)] public static T Freeze<T>(this IFixture fixture, T seed) 

Notice that this overload actually has EditorBrowsableState.Never, because it always confuses people. However, despite that, apparently people still find that overload, so I think it should be moved in AutoFixture 4. It's one of those features that exist because it was easy to implement...

like image 81
Mark Seemann Avatar answered Sep 19 '22 19:09

Mark Seemann


Freeze, Inject, and Register all are customizing the creation algorithm.

With Inject and Register you are specifying explicitly that an object should be created in a particular way, in your example by supplying new OrderLine("Foo") manually.

With Freeze you are not specifying how an object should be created - you ask AutoFixture to supply an instance for you.

In the end, all the above methods use the same lower-level API:

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


The reason why fixture.Freeze<OrderLine>(new OrderLine("Foo")); does not create an OrderLine instance with the specified seed value is because by default the seed is ignored.

To favor seed values of a particular type, you can create a 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;     } } 

Then you may use it as below:

fixture.Customizations.Add(     new SeedFavoringRelay<OrderLine>());  fixture.Freeze<OrderLine>(new OrderLine("Foo")); // -> Now fixture.Create<Order>() creates an Order with OrderLine's Name = "Foo". 
like image 35
Nikos Baxevanis Avatar answered Sep 21 '22 19:09

Nikos Baxevanis