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();
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();
If I understand the question correctly, I'll assume that we have classes like these:
Specifically, I've added the properties
Foo,Bar, andBazto each of those classes to emphasise that while one may be interested in settingx.First.Second.Thirdto 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:On
One:and on
Two:This enables you to use
Fixture'sGetextension method to produce anXvalue with a particularFirst.Second.Thirdvalue, but where all other values are populated freely by AutoFixture.The following test passes:
This uses an overload to
Fixture.Getthat takes a delegate with three input values. All those values are populated by AutoFixture, and you can then nest the copy-and-update methods usingx,first, andsecond.The assertions show that not only does
actual.First.Second.Thirdhave 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
firstandsecondvalues, sincexshould already contain those. Instead, you may want to be able to just 'reach into'First.Second.Thirdwithout 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:
A lens is a pair of functions. The
Getterreturns the value of a property, given a 'full' object. TheSetteris 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:
SetandGetsimply enables you to get the value of a property, or to set a property to a particular value. The interesting function here isCompose, which enables you to compose a lens fromTtoUwith a lens fromUtoV.This works best if you have static lenses defined for each class, for example for
X:One:Two: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:
You take
X.FirstLens, which is a lens fromXtoOneand first compose it withOne.SecondLens, which is a lens fromOnetoTwo. The result so far is a lens fromXtoTwo.Since this is a Fluent Inteface, you can keep going and compose this lens with
Two.ThirdLens, which is a lens fromTwotostring. The final, composed lens is a lens fromXtostring.You can then use the
Setextension method to set this lens onxto"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, wherefirst,second, andthirdare lenses.