Skip to content

Item Builder

The ItemBuilder is the entry point for configuring a Fixture Item. The ItemBuilder uses the builder pattern to provide a fluent API for configuring the dependencies of the instance requested. We call the requested instance Fixture Item in TWIZZAR.

Builder pattern

To get an instance of a type with TWIZZAR the ItemBuilder can be used:

c#
using Twizzar.Fixture;
var potion = new ItemBuilder<Potion>().Build();
using Twizzar.Fixture;
var potion = new ItemBuilder<Potion>().Build();

The Item builder follows the builder pattern, so when Build is called a new instance of the type is created. To configure the instance the With method can be used before calling Build.

c#
using Twizzar.Fixture;

// Generates a potion with the name: mana potion
var potion = new ItemBuilder<Potion>()
    .With(p => p.Name.Value("Mana Potion"))
    .Build();
using Twizzar.Fixture;

// Generates a potion with the name: mana potion
var potion = new ItemBuilder<Potion>()
    .With(p => p.Name.Value("Mana Potion"))
    .Build();

TIP

If you are using C#10 use global using Twizzar.Fixture; in your unit test project.

The With method can be used to configure members of the Fixture Item. The With method accepts a function which returns a MemberConfig. To create this MemberConfig TWIZZAR provides a path class as a parameter to the function to select and then configure a member.

Definition member

In TWIZZAR we consider the following as a member of a class, struct, interface or record:

  • field
  • property
  • method (only available for interfaces)
  • constructor parameter :::

The path class is automatically generated by the TWIZZAR. Analyzer with a Source Generator. Properties and fields can be selected by their name, constructor parameter can be selected with Ctor.<ParameterName> and methods can be selected by <MethodName>_<ReturnType>. We call it a path class because it is possible to access members further down in the dependency tree. When a potion with an ingredient with the name "Mana Potion" as the Ingredient1 property is required as show in this tree:

Potion
┗━ Ingredient1
   ┗━ Name: "Mana Potion"
Potion
┗━ Ingredient1
   ┗━ Name: "Mana Potion"

It can be selected over the path class by selecting the right path p.Ingredient1.Name. Or also over the constructor: p.Ctor.ingredient1.Name.

When we look at the Potion, it requires four additional parameters to be created.

c#
public class Potion
{
    public Potion(
        string name,
        IIngredient ingredient1,
        IIngredient ingredient2,
        IEffect effect,
        PotionColor color)
        {
            ...
        }
}
public class Potion
{
    public Potion(
        string name,
        IIngredient ingredient1,
        IIngredient ingredient2,
        IEffect effect,
        PotionColor color)
        {
            ...
        }
}

But we only configured the name. TWIZZAR provides for all not configured members a default behavior, for example for a string a unique value will be used and for an interface a mock object will be used. For classes or structs, the greatest constructor will be used and all its parameter will be resolved with the default behavior.

Definition of the greatest constructor

The greatest constructor is defined as the constructor with the most parameters. When there are more than one constructor with max parameters, then one is selected over a hash function by TWIZZAR. The selection is arbitrary but deterministic. This always means the same constructor is selected.

Dependency tree

With TWIZZAR dependencies get selected from top to down. This means when we select a member with a dependency path we go one layer deeper into the dependency tree for every member selection. If we have for example:

c#
class Potion{
    Ingredient Ingredient1 { get; set; }

}

class Ingredient {
    string Name { get; set; }

}
class Potion{
    Ingredient Ingredient1 { get; set; }

}

class Ingredient {
    string Name { get; set; }

}

To configure the Name member we use the path: Ingredient1.Name. This means we select the Ingredient1 member and then the Name member. The dependency tree looks like this:

INFO

To show the different layers of the dependency tree, click the buttons above.

Custom Item Builder

When a Fixture Item is configured via the Twizzar UI a new Custom Item Builder is declared, which requires the following steps:

  1. A new file is generated with the postfix .twizzar.cs for projects with the new csproj structure, the file will be nested under the unit test.
    Nested FilesNested Files
  2. The unit test class will be made partial.
  3. In the new generated file, a new partial class for the unit test class is generated
  4. A new Custom Item Builder class is generated in the generated file.
  5. The new ItemBuilder<T>() statement will be changed to new CustomItemBuilderName()
  6. For the CustomItemBuilderName a unique name will be generated.
Custom Item BuilderCustom Item Builder

TIP

Rename the auto generated name of the Custom Item Builder with the Visual Studio refactor option to something meaning full like ManaPotionBuilder.

The Custom Item Builder:

  • constructor body grows accordingly as you configure more items
  • allows custom code to be written inside it
  • is only visible to the unit test that created them
  • can be moved to different locations without affecting the TWIZZAR UI