Mocking Data with Bogus
Tags: dotnet, csharp, productivity, testing
Published: 2019-09-18 04:31:59

Ever needed a bunch of random data for testing, UI prototyping, or even as part of an application? I did, and I found a good option to generate it for .NET applications.

[Bogus](https://github.com/bchavez/Bogus) is loosely based off of the [Faker library for PHP](https://github.com/fzaninotto/Faker) and differs from testing libraries like [AutoFixture](https://github.com/AutoFixture/AutoFixture) by providing data that is closely analogous to what you might see in production. 

*If you're curious about AutoFixture, stay tuned as I plan on delving into the benefits that offers for testing purposes in a future article.*

I've written previously on using Bogus to [abstract away irrelevant values in unit testing](https://dev.to/integerman/refactoring-c-unit-tests-5bh6), but I want to show you taking it one step further and using it to generate entire objects or collections of objects.

{% link https://dev.to/integerman/refactoring-c-unit-tests-5bh6 %}

While this technique can help with unit test data generation, the code I'll be demoing here is actually intended as placeholder content while prototyping a user interface as well as for random content generation in a side project I'm working on.

---

In this tutorial, we're working under a scenario where I want to have a lot of randomly generated data representing the typical activities on a star ship (I said it was a [fun side project](https://dev.to/integerman/fun-project-starship-tech-support-angular-ngrx-1913), right?). 

Specifically, I need to generate crew members, a list of ship's systems that can break, and problems associated with random systems. All of these objects are wrapped up in a single `GameState` class which is a result value that I return back to the caller of my [.NET Core API](https://dev.to/integerman/creating-a-net-core-api-3n6d).

My code to generate a new game state is very minimal:

```cs
public GameState BuildNewGameState(int id)
{
    var state = new GameState(id);
    state.Time = new GameTime(8, 4, 2422, 3, 20);

    state.GenerateCrewMembers(8)
         .GenerateSystems(10)
         .GenerateWorkItems(15);

    return state;
}
```

The interesting bits here are a trio of extension methods I wrote against the `GameState` class that provide a fluent API for generating and configuring that object.

Let's start by taking a look at the `GenerateCrewMembers` method:

```cs
internal static GameState GenerateCrewMembers(this GameState state, int count)
{
    int nextId = state.NextCrewId;

    var faker = new Faker<CrewMember>()
        .RuleFor(c => c.Id, f => nextId++)
        .RuleFor(c => c.FirstName, f => f.Person.FirstName)
        .RuleFor(c => c.LastName, f => f.Person.LastName)
        .RuleFor(c => c.Department, f => f.PickRandom<Department>())
        .RuleFor(c => c.Rank, f => f.PickRandom<Rank>())
        .RuleFor(c => c.DaysInRank, f => f.Random.Int(0, 500));

    state.Add(faker.Generate(count));

    return state;
}
```

The first thing to note is that this is a `static` method in a `static` class and prefixes the first parameter with the `this` keyword. This makes the `GenerateCrewMembers` method act like an extension method where the object it was invoked on is passed in as the first parameter. This paired with returning the same `state` object at the end is what creates a fluent API type of environment allowing chaining multiple method calls together.

The bulk of what this method is doing is generating a `Faker` instance and configuring the rules for how that should behave. Each `RuleFor` passes a pair of lambda expressions - one for configuring the property on the object being constructed and the other for using faker to generate a value for that property.

For example the line `.RuleFor(c => c.FirstName, f => f.Person.FirstName)` tells Bogus that when it sees the `FirstName` field on the object being configured, it should use the Bogus faker `f`'s `Person.FirstName` property to generate a random first name from language-specific sets of data.

`Department` and `Rank` are both enums, so the `f.PickRandom<Rank>()` statement, for example, will grab a random `Rank` enum member and assign it to the `Rank` property on the object.

As we see with `.RuleFor(c => c.Id, f => nextId++)`, you don't have to use Bogus for generating a value. In my case I chose to generate a sequential list of numerical Ids.

And finally, if you do want a random number value, as I do in `DaysInRank`, the statement `.RuleFor(c => c.DaysInRank, f => f.Random.Int(0, 500));` will grab a random integer value between 0 and 500.

If you're a random number nerd and wondering what's going on under the covers for the number generation, Bogus is simply wrapping `System.Random` and calling in to an instance of that (which you can provide a seed for if you desire consistently random data).

---

Now that we have the crew generation, let's look at the `ShipSystem` generation code. This is trickier since `ShipSystem` has no parameter-less constructor and requires an int id to be passed in.

In this case, our code looks like this:

```cs
internal static GameState GenerateSystems(this GameState state, int count)
{
    int nextId = state.NextSystemId;

    var faker = new Faker<ShipSystem>()
        .CustomInstantiator(f => new ShipSystem(nextId++))
        .RuleFor(c => c.Name, f => f.Commerce.ProductName());

    state.Add(faker.Generate(count));

    return state;
}
```

The first difference here is that the `CustomInstantiator` line allows you to configure how the instance is created. Thankfully the syntax for this is as simple as invoking a constructor, which I find preferable to trying to remember the parameter ordering and just passing in an array of objects as you would in using Reflection to instantiate objects, for example.

The second difference is that we're using the `Faker` instance's `Commerce.ProductName` property. This generates marvelous gems such as *Rustic Concrete Pizza* and *Handmade Granite Chicken*, among others. While not exactly star ship system names, it'll do for testing and prototyping.

An additional note here - if we wanted to make sure that every public property on the type being created was set, we could add in a call to `.StrictMode(true)` on the `Faker<T>` instance. This will raise exceptions if new properties are created that no rule was configured for.

---

Finally, let's get to configuring the `WorkItems` associated with the ship. This is an interesting collection because it interacts with both the Crew and the Systems collection in order to attribute work items to a crew member and problems with an impacted system.

The code is as follows:

```cs
internal static GameState GenerateWorkItems(this GameState state, int count)
{
    int nextId = 1;
    if (state.WorkItems.Any())
    {
        nextId = state.WorkItems.Max(c => c.Id) + 1;
    }

    var faker = new Faker<WorkItem>()
        .CustomInstantiator(f => new WorkItem(nextId++))
        .RuleFor(c => c.Title, f => f.Hacker.Phrase())
        .RuleFor(c => c.Department, f => f.PickRandom<Department>())
        .RuleFor(c => c.Status, f => WorkItemStatus.New)
        .RuleFor(c => c.Priority, f => f.PickRandom<Priority>())
        .RuleFor(c => c.WorkItemType, f => WorkItemType.Incident)
        .RuleFor(c => c.CreatedByCrewId, f => f.PickRandom(state.Crew).Id)
        .RuleFor(c => c.SystemId, f => f.PickRandom(state.Systems).Id)
        .RuleFor(c => c.AssignedCrewId, f => f.PickRandom(state.Crew.Select(c => c.Id))
                                              .OrNull(f, 0.85f));

    state.Add(faker.Generate(count));

    return state;
}
```

Here everything is similar to what we've seen before with a few exceptions.

With `CreatedByCrewId` and `SystemId`, we're using `PickRandom` to pick a random member of a collection and then access a property on that entry.

`AssignedCrewId` works similarly, but I also want to support null values and expect them most of the time, so I select the Id property from each member of `Crew` and then call the `Faker` extension `OrNull` passing in the `Faker` instance and a percentage between 0 and 1 for how likely it should be that the value is null. Faker will then use this to vary up the values for assignment.

Finally, Title uses `Hacker.Phrase` for content generation. This is probably my favorite generator in Bogus that I've worked with and spits out lines that you might find on crime scene investigation TV shows. Here are a few of my favorites:

> We need to copy the 1080p JSON firewall!

> I'll navigate the virtual RAM port, that should port the RAM port!

> I'll copy the haptic SSL system, that should system the SSL system!

*Side note: I want a haptic SSL system if anyone has one to spare.*

When I put it all together and run a scaled down version of the generation process, I get this lovely output:

```json
[
  {
    "workItems":[
      {
        "title":"Try to synthesize the AI microchip, maybe it will synthesize the virtual microchip!",
        "id":1,
        "department":0,
        "assignedCrewId":null,
        "createdByCrewId":2,
        "systemId":1,
        "workItemType":0,
        "status":0,
        "priority":3
      }
    ],
    "crew":[
      {
        "firstName":"Rogelio",
        "lastName":"Morissette",
        "rank":13,
        "department":0,
        "daysInRank":253,
        "id":1
      },
      {
        "firstName":"Isabel",
        "lastName":"Langosh",
        "rank":12,
        "department":5,
        "daysInRank":184,
        "id":2
      }
    ],
    "systems":[
      {
        "id":1,
        "name":"Practical Plastic Bike"
      },
      {
        "id":2,
        "name":"Rustic Concrete Towels"
      },
      {
        "id":3,
        "name":"Sleek Concrete Computer"
      }
    ],
  }
]
```

While this is largely random, it is useful data for prototyping and testing purposes and that helps me out quite a bit.

Bogus can do a lot more than this with its multiple language support, URL generation methods, database strings, colors, user reviews, mac addresses, E-Mail addresses, phone numbers, and even cultural specific things like US social security numbers, Canada SINs, and Great Britain vehicle registration plates.

Hopefully this shows you how a library like Bogus can speed up your workflow in prototyping or testing complex data.

---

*Cover Photo by Eric Prouzet on Unsplash*