Curtis Carter
CodingCoyote Blog

CodingCoyote Blog

Generating Realistic Test Data with Bogus.Net

Curtis Carter's photo
Curtis Carter
·Aug 9, 2021·

6 min read

Bogus is a .Net library for generating test data that uses the datasets of faker.js. This goes a few steps beyond simply accessing random entries from a collection and using them in a test. Bogus facilitates writing generators with complex rulesets to generate realistic and contextual test data for demos or E2E tests.

License?

Before using or suggesting any library, you need to understand it's license. Luckily the license for Bogus.Net is MIT/ BSD(3-clause) meaning you can use it personally, use it for commercial use, and even modify and redistribute it without worrying about the license on your own project.

What Can Bogus Do For Me?

Let's cover a simple object that most people probably have: User.

public class User
{
   public string Username;
   public string FirstName;
   public string LastName;
   public string UserAddress;
}

We can use bogus to create a faker that generates users on demand. There's a couple syntax options for this but I generally use the Rules method to add a set of complex rules for data generation.

public Faker<User> testDataGenerator = new Faker<User>((faker, user) => {
   var person = new Bogus.Person();
   user.UserName = person.Email;
   user.FirstName = person.FirtName;
   user.LastName = person.LastName;
user.Address = faker.Address.FullAddress();
};

Why did we make a Person instead of just using values from the Email and Name datasets? This will generate an email address based on the Person created.

Why Not Roll My Own Fake Data?

Fake data generation is easy and somewhat trivial. Ransome numbers and text could be created for almost any class. So why should you bother learning a specific API that offers realistic data? Demoing a project to users, investors, or your coworkers is the more apparent use.

Realistic data makes a project look more professional. Think about what Netflix would feel like if all the pictures were randomly colored blocks and all the titles and descriptions were just random lengths of text? Not very, pretty is it? What about Twitter? Random keyboard mashing instead of realistic Tweets? The image is more clear when you see data that makes sense. Even Lorem Ipsum text is better since at a glance it looks like real text. Without having to imagine real text and imagery and deciphering where real images would sit, they can focus on the look, feel and function of your app.

And even if you're just making something for yourself, the benefit to testing is colossal. Not using realistic data can cause you to not notice unexpected truncation, displaying the wrong field etc. You may be tempted to set all your products up with names like "Product 1", "Product 2" and prices such as $0.01 and $0.02 respectively. but with Bogus you can have "Tasty Fresh Cheese", "Handcrafted Steel Chair" and "Ergonomic Granite Shirt". Ok, so not all of them are super realistic. Still better than "Product 4052". My coworkers regularly get a laugh from some of our more absurd bits of data and have even caught issues due to the attention these caused.

Bogus has a whole host of datasets for almost any use. And Premium Bogus extensions add even more support for specific fields such as healthcare or movies and TV shows and even GPS data that offer contextual data for demoing projects or testing.

Can't get approval to purchase a license? The community has written several extensions that add datasets such as seemingly realistic random prose . And Bogus provides all the tools you need to add your own datasets instead of engineering your own system to handle them.

But How Do I Test Classes With This

If your first instinct was to add a Faker field to every object you want to test, STOP! Polluting production code with test code is rarely a good idea.

I personally have static "generators" that I use to generate data with Bogus:

    public static class TestData
    {
        public static Faker<User> UserGenerator = new Faker<User>().Rules((faker, user)=>
        {
            var person = new Bogus.Person();
            user.Username = person.UserName;
            user.Email = person.Email;
            user.FirstName = person.FirstName;
            user.LastName = person.LastName;
            user.UserAddress = faker.Address.FullAddress();
        });
    }

    public class TestClass
    {
        public void GenreateUsers()
        {
            var user = TestData.UserGenerator.Generate();
            var userArray = TestData.UserGenerator.GenerateLazy(500).ToArray();
            var userList = TestData.UserGenerator.GenerateBetween(1, 5000).ToList();
        }
    }

Using Generate, I can get a single test user. I can plug this user into the test and try creating an account, adding items to the cart, or starting a collaborative session (or whatever your applicatin does). But what if I need to run a report for 500 users? GenerateLazy Lets you Generate a specific number of users using LINQ's deferred execution just like a calling to a database so you can take any other actions before retrieving an array (consider using a db mocking framework for actual database related test). Generate forever would give you the same results, but generates the users up front.

Not sure how many Users to create? Need to make sure no one "fixes" a test by targeting the 500 users you are creating. GenerateBetween can get a quantity within your limits. Keeping it random while still large enough to matter and small enough to not pushing the limits of scalability.

What if you use the same user class for free and paid users in addition to users that are staff, admins and moderators? Maybe you just need more targeted data for some tests. Bogus supports that too. Bogus RuleSets allow writing specific rules for each set of generators you need. ("default" is internally applied to any rules without a ruleset)

public static Faker<User> UserGenerator = new Faker<User>().Rules((faker, user) =>
    {
         var person = new Bogus.Person();
         user.Username = person.UserName;
         user.Email = person.Email;
         user.FirstName = person.FirstName;
         user.LastName = person.LastName;
         user.UserAddress = faker.Address.FullAddress();
        user.Status = faker.Random.Enum<Statuses>();
     }).RuleSet("FreeUsers", set =>
        {
            set.Rules((faker, user) =>
            {
                var person = new Bogus.Person();
                user.Username = person.UserName;
                user.Email = person.Email;
                user.FirstName = person.FirstName;
                user.LastName = person.LastName;
                user.UserAddress = faker.Address.FullAddress();
                user.Status = Statuses.Free;
            });
    }).RuleSet("DeactivatedUsers", set =>
   {
      set.Rules((faker, user) =>
      {
          var person = new Bogus.Person();
          user.Username = person.UserName;
          user.Email = string.Empty;
          user.FirstName = person.FirstName;
          user.LastName = person.LastName;
          user.UserAddress = faker.Address.FullAddress();
         user.Status = Statuses.Deactivated;
    });
});

public class TestClass
{
    public void GenreateUsers(string ruleset = "default")
    {
        var user = TestData.UserGenerator.Generate();
        var userArray = TestData.UserGenerator.GenerateLazy(500).ToArray();
        var userList = TestData.UserGenerator.GenerateBetween(1, 5000).ToList();
        var freeUser = TestData.UserGenerator.Generate("FreeUsers");
        var deactivatedUser = TestData.UserGenerator.Generate("DeactivatedUser");
        var RulesetPassedInUser = TestData.UserGenerator.Generate(ruleset);
    }
}

The Community Is Awesome

Having trouble? Found a bug? Want to request a feature? The Bogus community is awesome. Although the maintainer is picky (as one should be) about what gets pulled into Bogus itself, both bchavez and the rest of the community is active in helping users to extend Bogus for their own purposes or in publishing an extension that is versatile enough for other projects to consume as well.

Support Bogus.Net Development

Normally at the end of each post, I include my GitHub sponsor link. Bogus has been one of my favorite open source projects for some time and was the source rod one of my first Pull Requests. Show the project some love and sponsor bchavez.

Did you find this article valuable?

Support Curtis Carter by becoming a sponsor. Any amount is appreciated!

Learn more about Hashnode Sponsors
 
Share this