Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to build nested property with autofixture

Tags:

c#

autofixture

How to set nested property with autofixture (it's readonly)? Something like this:

var result =
    fixture.Build<X>()
    .With(x => x.First.Second.Third, "value")
    .Create();
like image 720
user963935 Avatar asked Jun 19 '15 13:06

user963935


1 Answers

If I understand the question correctly, I'll assume that we have classes like these:

public class X
{
    public X(One first, string foo)
    {
        First = first;
        Foo = foo;
    }

    public One First { get; }

    public string Foo { get; }
}

public class One
{
    public One(Two second, int bar)
    {
        Second = second;
        Bar = bar;
    }

    public Two Second { get; }

    public int Bar { get; }
}

public class Two
{
    public Two(string third, bool baz)
    {
        Third = third;
        Baz = baz;
    }

    public string Third { get; }

    public bool Baz { get; }
}

Specifically, I've added the properties Foo, Bar, and Baz to each of those classes to emphasise that while one may be interested in setting x.First.Second.Third to a specific value, one would still be interested in having all other properties populated by AutoFixture.

As a general observation, once you start working with immutable values, this is where a language like C# starts to reveal its limitations. While possible, it goes against the grain of the language.

There's plenty of other advantages to writing code with immutable data, but it gets tedious in C#. That's one of the reasons I finally gave up on C# and moved on to F# and Haskell. While this is a bit of a digression, I mention this to explicitly communicate that I think that using read-only properties is a fine design decision, but that it comes with some known problems.

In general, when working with immutable values, particularly in testing, it's a good idea to add copy-and-update methods to each immutable class, starting with X:

public X WithFirst(One newFirst)
{
    return new X(newFirst, this.Foo);
}

On One:

public One WithSecond(Two newSecond)
{
    return new One(newSecond, this.Bar);
}

and on Two:

public Two WithThird(string newThird)
{
    return new Two(newThird, this.Baz);
}

This enables you to use Fixture's Get extension method to produce an X value with a particular First.Second.Third value, but where all other values are populated freely by AutoFixture.

The following test passes:

[Fact]
public void BuildWithThird()
{
    var fixture = new Fixture();

    var actual =
        fixture.Get((X x, One first, Two second) =>
            x.WithFirst(first.WithSecond(second.WithThird("ploeh"))));

    Assert.Equal("ploeh", actual.First.Second.Third);
    Assert.NotNull(actual.Foo);
    Assert.NotEqual(default(int), actual.First.Bar);
    Assert.NotEqual(default(bool), actual.First.Second.Baz);
}

This uses an overload to Fixture.Get that takes a delegate with three input values. All those values are populated by AutoFixture, and you can then nest the copy-and-update methods using x, first, and second.

The assertions show that not only does actual.First.Second.Third have the expected value, but all other properties are populated as well.

Lenses

You may think that it seems redundant that you have to ask AutoFixture for the first and second values, since x should already contain those. Instead, you may want to be able to just 'reach into' First.Second.Third without having to deal with all of those intermediary values.

This is possible using lenses.

A lens is a construct with the origin in category theory, and used in some programming languages (most notably Haskell). Functional programming is all about immutable values, but even with functional languages with first-class support for immutable data, deeply nested immutable records are awkward when you just need to update a single datum.

I don't intend to turn this answer into a lenses tutorial, so if you really want to understand what's going on, search for a lenses tutorial in your favourite functional programming language.

In short, though, you can define a lens in C# like this:

public class Lens<T, V>
{
    public Lens(Func<T, V> getter, Func<V, T, T> setter)
    {
        Getter = getter;
        Setter = setter;
    }

    internal Func<T, V> Getter { get; }

    internal Func<V, T, T> Setter { get; }
}

A lens is a pair of functions. The Getter returns the value of a property, given a 'full' object. The Setter is a function that takes a value, and an old object, and returns a new object with the property changed to the value.

You can define a set of functions that operate on lenses:

public static class Lens
{
    public static V Get<T, V>(this Lens<T, V> lens, T item)
    {
        return lens.Getter(item);
    }

    public static T Set<T, V>(this Lens<T, V> lens, T item, V value)
    {
        return lens.Setter(value, item);
    }

    public static Lens<T, V> Compose<T, U, V>(
        this Lens<T, U> lens1,
        Lens<U, V> lens2)
    {
        return new Lens<T, V>(
            x => lens2.Get(lens1.Get(x)),
            (v, x) => lens1.Set(x, lens2.Set(lens1.Get(x), v)));
    }
}

Set and Get simply enables you to get the value of a property, or to set a property to a particular value. The interesting function here is Compose, which enables you to compose a lens from T to U with a lens from U to V.

This works best if you have static lenses defined for each class, for example for X:

public static Lens<X, One> FirstLens =
    new Lens<X, One>(x => x.First, (f, x) => x.WithFirst(f));

One:

public static Lens<One, Two> SecondLens =
    new Lens<One, Two>(o => o.Second, (s, o) => o.WithSecond(s));

Two:

public static Lens<Two, string> ThirdLens =
    new Lens<Two, string>(t => t.Third, (s, t) => t.WithThird(s));

This is boilerplate code, but it's straightforward once you get the hang of it. Even in Haskell it's boilerplate, but it can be automated with Template Haskell.

This enables you to write the test using a composed lens:

[Fact]
public void BuildWithLenses()
{
    var fixture = new Fixture();

    var actual = fixture.Get((X x) =>
        X.FirstLens.Compose(One.SecondLens).Compose(Two.ThirdLens).Set(x, "ploeh"));

    Assert.Equal("ploeh", actual.First.Second.Third);
    Assert.NotNull(actual.Foo);
    Assert.NotEqual(default(int), actual.First.Bar);
    Assert.NotEqual(default(bool), actual.First.Second.Baz);
}

You take X.FirstLens, which is a lens from X to One and first compose it with One.SecondLens, which is a lens from One to Two. The result so far is a lens from X to Two.

Since this is a Fluent Inteface, you can keep going and compose this lens with Two.ThirdLens, which is a lens from Two to string. The final, composed lens is a lens from X to string.

You can then use the Set extension method to set this lens on x to "ploeh". The assertions are the same as above, and the test still passes.

The lens composition looks verbose, but that's mainly an artefact of C# limited support for custom operators. In Haskell, a similar composition would literally look like first.second.third, where first, second, and third are lenses.

like image 54
Mark Seemann Avatar answered Nov 03 '22 21:11

Mark Seemann