Unit And Benchmark Testing With Random Data In .NET 6 And 7

In 2019, while I was working on benchmark tests for my new book on code & app performance, I wanted to use “real-world” data types like a person or a coordinate along with methods for creating random words, email addresses, URLs, etc. After I worked on the code, I thought that most of it could be re-used by myself in other projects, so I moved it into an assembly. Then I thought others might like to use it so then I turned it into a NuGet package!

For .NET 6 & 7, I have made many changes to the assembly and NuGet package called DotNetTips.Utility.Standard.Tester. The main goal is to make it simple to create these real-world objects along with lots of other methods to create random data. Some of the methods include the use of fixed-length strings that I needed for the benchmark tests. I’m even starting to use this assembly on projects where I work.

Next, I will describe some of the methods and objects that are part of the assembly.

Person Types

For the benchmark tests for my performance book, I created three Person types that reflect different ways that I see developers create data model classes. All of them implement from the IPerson interface, except for the PersonRecord which implements IDataRecord, that defines these properties:

Address1 Address2 BornOn
CellPhone City Country
Email FirstName HomePhone
Id LastName PostalCode
State    

Unit and Benchmark Testing with Random Data in .NET 6 & 7

The two different types that implement IPerson are,

Person

This type represents how I see most model classes created for use in web API service calls or Entity Framework by implementing the properties in IPerson as auto-implemented properties.

PersonProper

This type adds proper validation to all appropriate properties. It also uses the Serializable, XmlRoot, and DataContract attributes. The type represents how most data objects should be implemented and should usually be the one that you should use in your tests. There is also a duplicate of this type as a value type called Person in the ValueTypes namespace.

PersonRecord

This type is implemented exactly as PersonProper but as a record class. The major difference is that this type allows for multiple addresses by using the AddressRecord type.

Coordinate Types

Also, for my benchmark tests, I created two structure types that implement the ICoordinate interface. The interface has only two properties, X and Y. The two different types that implement ICoordinate are,

Coordinate

This structure implements X and Y as auto-implemented properties. It uses the Serializable attribute.

CoordinateProper

This structure is implemented the same as Coordinate. It implements the IEquatable<>, IComparable, and IComparable<CoordinateProper> interfaces. It also overrides ToString(), Equals(), and GetHashCode(). It implements operators (since structures do not have them by default) and uses the Serializable attribute. This structure should be used most often in your tests and is an example of how to properly implement a structure.

Random Data Methods

Using random data is very important if you are testing code in your assemblies. I don’t know how many times in the past I forgot to test the last name value that includes an apostrophe which can cause SQL Server inserts or updates to fail. Humans aren’t very good at coming up with random data, code can solve that.

I created the RandomData static type that helps with generating random data. There are many methods in this class, and I add new ones often, especially when working on a new edition of my books. The methods are listed below along with sample output (most from using ? in the Immediate Window in Visual Studio).

Method Output
GenerateAddressRecordCollection() Generates 1 to many random AddressRecord's to be used in the PersonRecord type.
GenerateByteArray() This method generates a random byte array.
GenerateCharacter() Generates a random character.
Example: 82 'R'
GenerateCoordinate() Generates any type that implements ICoordinate.
GenerateCoordinateCollection() This method will generate 1 to many of any type that implements ICoordinate.
GenerateDecimal() Generates a random decimal.
GenerateDomainExtension() Generates a random domain extension.
Example: .co.uk
GenerateEmailAddress() Generates a random email address.
GenerateFile() Generates a file in the AppData folder.
GenerateFiles() Generates 1 to many files In the AppData folder.
GenerateInteger() Generates a random integer.
GenerateKey() Generates a random unique key.
Example: f7f0af78003d4ab194b5a4024d02112a
GenerateNumber() Generates a random number as a string.
Example: 446085072052112
GeneratePersonRefCollection() Generates 1 to many of any reference type that implements IPerson.
GeneratePersonValCollection() Generates 1 to many of any value type that implements IPerson.
GeneratePhoneNumberUSA() Generates a random phone number using the USA format.
Example: 284-424-2216
GenerateRandomFileName() Generates a random file name using the temp folder.
Example: c:\\temp\0yiv4iiu.uuv
GenerateRefPerson() Generates any type that implements IPerson as a reference type.
GenerateRelativeUrl() Generates a random relative url.
Example: /ljsylu/rsglcurkiylqld/wejdbuainlgjofnv/uwbrjftyt/
GenerateTempFile() Generates a temporary file In the
C:\\Users\<user name>\AppData\Local\Temp\ folder. File length can be defined.
GenerateUrl() Generates a random URL.
Example: https://www.agngbgluhawxhnmoxvdogla.hdtmdjmiagwlx.com/r/ulhekwhqnicq/
GenerateUrlHostName() Generates a random URL host name.
Example: https://www.ehvjnbhcpcivgiccugim.lfa.net
GenerateUrlHostnameNoProtocol() Generates a random URL host name with no protocol.
Example: www.wucqcapnybi.kejdwudpbstekhxic.co.uk
GenerateUrlHostnameNoSubdomain() Generates a random URL host name with no subdomain.
Example: elqqcw.org.uk
GenerateUrlPart() Generates a random URL part.
Example: /rregyyjxpjiats
GenerateValPerson() Generates any type that implements IPerson as a value type.
GenerateWord() Generates a random word.
Example: mL_g[E_E_CsoJvjshI]CFjFKa
GenerateWords() Generates 1 to many random words.
Example:
[0]: "oKcMYETNvpiByRQVa^"
[1]: "mnM\\wQwuluQ^VFxpOJEgLX"
[2]: "Ad\\kCOMkmdK"
LongTestString Returns a long string to be used in tests.

Usage Examples

To install the NuGet package, run the following from the Package Manager Console in Visual Studio,

NuGet\Install-Package DotNetTips.Spargine.6.Tester. Or go to: https://www.nuget.org/profiles/davidmccarter

This is an example of how I use this package in my benchmark projects.

var array = RandomData.GenerateWords(25, minLength: 15, maxLength: 15);
var person = RandomData.GeneratePersonRecordCollection(count: 1, addressCount: 1).First();
var array = RandomData.GenerateByteArray(sizeInKb: 1);
var result = RandomData.GenerateDecimal(minValue: 0, maxValue: 1000, decimalPlaces: 2);

Here is how I use it in a unit test project.

[TestMethod]
public void GetCashedItemTest() 
{
    var result = InMemoryCache.Instance;
    var person = RandomData.GenerateRefPerson < PersonProper > ();
    result.AddCacheItem(person.Id, person);
    var item = result.GetCacheItem < PersonProper > (person.Id);
    Assert.IsNotNull(item);
}

Summary

I hope that you will check out the DotNetTips.Utility.Standard.Tester NuGet package for use in your testing projects. Need something added? I hope you will contribute to the project on GitHub. If you use this assembly in your unit test or benchmark projects, I’d like to hear from you and how you liked it.


Similar Articles
McCarter Consulting
Software architecture, code & app performance, code quality, Microsoft .NET & mentoring. Available!