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.
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, but I want to show you taking it one step further and using it to generate entire objects or collections of objects.
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, 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.
My code to generate a new game state is very minimal:
|public GameState BuildNewGameState(int id)|
|var state = new GameState(id);|
|state.Time = new GameTime(8, 4, 2422, 3, 20);|
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
|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));|
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
Person.FirstName property to generate a random first name from language-specific sets of data.
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:
|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());|
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
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:
|internal static GameState GenerateWorkItems(this GameState state, int count)|
|int nextId = 1;|
|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))|
Here everything is similar to what we’ve seen before with a few exceptions.
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
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:
|"title":"Try to synthesize the AI microchip, maybe it will synthesize the virtual microchip!",|
|"name":"Practical Plastic Bike"|
|"name":"Rustic Concrete Towels"|
|"name":"Sleek Concrete Computer"|
While this is very 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.