Autofixture: Customize with a statement lambda does not work - is it expected?

Created on 7 Jun 2018  ·  2Comments  ·  Source: AutoFixture/AutoFixture

The following test fails:

public class UnitTest1
    {
        [Fact]
        public void Test1()
        {
            var fixture = new Fixture();
            fixture.Customize<PaymentReturnPayload>(c =>
            { // a statement lambda
                c.With(a => a.OperationAmount, "1");
                c.FromFactory(() => new PaymentReturnPayload(fixture.Create<int>().ToString()));
                return c;
            });
            var paymentReturnPayload = fixture.Create<PaymentReturnPayload>();
            Assert.True(int.TryParse(paymentReturnPayload.Control, out _));
        }
    }

    public class PaymentReturnPayload
    {
        public PaymentReturnPayload(string control)
        {
            Control = control;
        }

        public string OperationAmount { get; set; }

        public string Control
        {
            get;
        }
    }

The Control parameter is populated by a random string instead of generated int.

Version 4.0 - 4.4 ( I didn't test previous ones)
Net core 2.0 - 2.1

This works well

[Fact]
        public void Test1()
        {
            var fixture = new Fixture();
            fixture.Customize<PaymentReturnPayload>(c =>
                c.FromFactory(() => new PaymentReturnPayload(fixture.Create<int>().ToString())));
            var paymentReturnPayload = fixture.Create<PaymentReturnPayload>();
            Assert.True(int.TryParse(paymentReturnPayload.Control, out _));
        }

It looks like customizing using a statement lambda does not work.
Is it an expected behaviour?
And if yes, shouldn't it be added to some documentation? I lost lot of time figuring out what is wrong...

question

All 2 comments

@progala2 Thanks for raising the question.

The existing behavior is by design. The reason is that FromFactory, With (and Without) methods are immutable, so they don't change the underlying object. Instead, they return a new object with configured graph.

The reason why the first test doesn't work is that you don't use the result of the invocations, so all the applied changes are discarded. Once you rewrite the code correctly, it will work fine with statements as well:

```c#
[Fact]
public void Test1()
{
var fixture = new Fixture();
fixture.Customize(c =>
{
// a statement lambda
var res = c.FromFactory(() => new PaymentReturnPayload(fixture.Create().ToString()));
res = res.With(a => a.OperationAmount, "1");
return res;
});

var paymentReturnPayload = fixture.Create<PaymentReturnPayload>();
Assert.True(int.TryParse(paymentReturnPayload.Control, out _));

}


But as you might notice, it's easier to write this code with lambda.

P.S. By the way, you can slightly improve the final code (notice the `FromFactory` has overload taking extra values necessary for the object construction):
```c#
[Fact]
public void Test3()
{
    var fixture = new Fixture();
    fixture.Customize<PaymentReturnPayload>(c =>
        c.FromFactory((int control) => new PaymentReturnPayload(control.ToString())));

    var paymentReturnPayload = fixture.Create<PaymentReturnPayload>();
    Assert.True(int.TryParse(paymentReturnPayload.Control, out _));
}

I see, they are immutable - it's much clear now.
Thank you for your answer and some additional tips!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

JoshKeegan picture JoshKeegan  ·  6Comments

Accc99 picture Accc99  ·  4Comments

gtbuchanan picture gtbuchanan  ·  3Comments

joelleortiz picture joelleortiz  ·  4Comments

DeafLight picture DeafLight  ·  5Comments