This collection of randomization test utilities supports creating test arrangements.
See API Documentation.
All examples below use the thread-safe, static
Jds.TestingUtils.Randomization.Randomizer.Shared
instance ofIRandomizationSource
, which generates random values usingSystem.Random.Shared
. ThisIRandomizationSource
is advised for most tests.If a different algorithm is needed, e.g., a cryptographically strong random number generator is required, consider creating a
Jds.TestingUtils.Randomization.ArrangedRandomizationSource
. It uses providedFunc<IEnumerable<>>
delegates to supply values when requested.If a specific set of random-seeming data is needed, consider creating a
Jds.TestingUtils.Randomization.DeterministicRandomizationSource
. It uses providedIEnumerable<>
sources to supply values when requested.
Add Jds.TestingUtils.Randomization
NuGet package to the test project.
Add the extensions to your test files with the following using
statement:
using Jds.TestingUtils.Randomization;
All examples below assume the following additional using
statements:
using System;
using System.Collections.Generic;
using System.Linq;
IRandomizationSource.Boolean()
- Gets a pseudo-random
bool
.
- Gets a pseudo-random
IRandomizationSource.Byte()
- Gets a pseudo-random
byte
, greater than or equal tobyte.MinValue
, and less thanbyte.MaxValue
.
- Gets a pseudo-random
IRandomizationSource.ByteInRange(byte minInclusive, byte maxExclusive)
- Gets a pseudo-random
byte
, greater than or equal tominInclusive
, and less thanmaxExclusive
.
- Gets a pseudo-random
IRandomizationSource.Double()
- Gets a pseudo-random
double
, usingIRandomizationSource.NextDouble
. The value should be greater than or equal to0.0
, and less than1.0
.
- Gets a pseudo-random
IRandomizationSource.Float()
- Gets a pseudo-random
float
, usingIRandomizationSource.NextFloat
. The value should be greater than or equal to0.0
, and less than1.0
.
- Gets a pseudo-random
IRandomizationSource.Int()
- Gets a pseudo-random
int
, usingIRandomizationSource.NextIntInRange
. The value should be greater than or equal toint.MinValue
, and less thanint.MaxValue
.
- Gets a pseudo-random
IRandomizationSource.IntInRange(int minInclusive, int maxExclusive)
- Gets a pseudo-random
int
, usingIRandomizationSource.NextIntInRange
. The value should be greater than or equal tominInclusive
, and less thanmaxExclusive
.
- Gets a pseudo-random
IRandomizationSource.IntNegative()
- Gets a pseudo-random
int
, usingIRandomizationSource.NextIntInRange
. The value should be greater than or equal toint.MinValue
, and less than0
.
- Gets a pseudo-random
IRandomizationSource.IntPositive()
- Gets a pseudo-random
int
, usingIRandomizationSource.NextIntInRange
. The value should be greater than or equal to0
, and less thanint.MaxValue
.
- Gets a pseudo-random
IRandomizationSource.Long()
- Gets a pseudo-random
long
, usingIRandomizationSource.NextLongInRange
. The value should be greater than or equal tolong.MinValue
, and less thanlong.MaxValue
.
- Gets a pseudo-random
IRandomizationSource.LongInRange(long minInclusive, long maxExclusive)
- Gets a pseudo-random
long
, usingIRandomizationSource.NextLongInRange
. The value should be greater than or equal tominInclusive
, and less thanmaxExclusive
.
- Gets a pseudo-random
IRandomizationSource.LongNegative()
- Gets a pseudo-random
long
, usingIRandomizationSource.NextLongInRange
. The value should be greater than or equal tolong.MinValue
, and less than0
.
- Gets a pseudo-random
IRandomizationSource.LongPositive()
- Gets a pseudo-random
long
, usingIRandomizationSource.NextLongInRange
. The value should be greater than or equal to0
, and less thanlong.MaxValue
.
- Gets a pseudo-random
IRandomizationSource.UShort()
- Gets a pseudo-random
ushort
.
- Gets a pseudo-random
IRandomizationSource.Enumerable<T>(Func<T> factory, int inclusiveMinCount, int exclusiveMaxCount)
IRandomizationSource.Enumerable<T>(Func<int, T> factory, int inclusiveMinCount, int exclusiveMaxCount)
- Creates an
IEnumerable<T>
of a randomly-generated length using values provided by a factory method. - Example:
Randomizer.Shared.Enumerable(Guid.NewGuid, 3, 7)
- which would generate a sequence of3
to6
Guid
values.
- Creates an
IRandomizationSource.RandomEnumValue<TEnum>()
- Selects a pseudo-random value from the
enum
of type specified (TEnum
). - Example:
Randomizer.Shared.RandomEnumValue<System.Net.HttpStatusCode>()
- Selects a pseudo-random value from the
IRandomizationSource.RandomListItem<T>(IReadOnlyList<T> items)
IReadOnlyList<T>.GetRandomItem<T>()
- Selects a pseudo-random item from provided
items
. - Example:
Randomizer.Shared.RandomListItem(System.Linq.Enumerable.Range(1, 20).ToArray())
- Selects a pseudo-random item from provided
IRandomizationSource.RandomListItem<T>(IReadOnlyList<(T Item, double Weight)> weightedItems)
IRandomizationSource.RandomListItem<T>(IReadOnlyList<(T Item, int Weight)> weightedItems)
IReadOnlyList<(T Key, double Weight)>.GetWeightedRandomItem<T>()
IReadOnlyList<(T Key, int Weight)>.GetWeightedRandomItem<T>()
- Selects a pseudo-random
.Item
from providedweightedItems
; item selection is weighted based upon relative.Weight
. - Example:
- Selects a pseudo-random
Randomizer.Shared.WeightedRandomListItem(
new[] { ("Sure", 1000), ("Likely", 500), ("Possible", 200), ("Unlikely", 50), ("Rare", 5), ("Apocryphal", 1) }
);
IRandomizationSource.GetWeightedRandomKey<T>(IReadOnlyDictionary<T, double> weightedKeys)
IRandomizationSource.GetWeightedRandomKey<T>(IReadOnlyDictionary<T, int> weightedKeys)
IReadOnlyDictionary<T, double>.GetWeightedRandomKey<T>()
IReadOnlyDictionary<T, int>.GetWeightedRandomKey<T>()
- Selects a pseudo-random
.Key
from providedweightedItems
; item selection is weighted based upon relative.Value
. - Example:
- Selects a pseudo-random
Randomizer.Shared.WeightedRandomKey(new Dictionary<string, double>
{
{ "North", 0.4 }, { "East", 0.1 }, { "West", 0.1 }, { "South", 0.4 }
});
IRandomizationSource.RandomString(int length, IReadOnlyList<char> chars)
- Generates a pseudo-random
string
oflength
characters, using providedchars
. Random selections fromchars
are concatenated until reachinglength
characters.
- Generates a pseudo-random
IRandomizationSource.RandomString(int length, IReadOnlyList<string> strings)
- Generates a pseudo-random
string
oflength
characters, using providedstrings
. Random selections fromstrings
are concatenated until reachinglength
characters. The result is truncated tolength
characters.
- Generates a pseudo-random
IRandomizationSource.RandomStringLatin(int length, bool uppercase = false, bool alphanumeric = false)
- Generates a pseudo-random
string
oflength
characters using ASCII Latin characters. Usesa
-z
by default. Ifuppercase
, usesA
-Z
. Ifalphanumeric
, also includes0
-9
with either casing.
- Generates a pseudo-random
IRandomizationSource.Hexadecimal(int length)
- Generates a pseudo-random hexadecimal (
0
throughf
, lowercase) string.
- Generates a pseudo-random hexadecimal (
IRandomizationSource.HashSha256()
- Generates a
64
-character hexadecimal (0
throughf
, lowercase) string.
- Generates a
IRandomizationSource.HashSha384()
- Generates a
96
-character hexadecimal (0
throughf
, lowercase) string.
- Generates a
IRandomizationSource.HashSha512()
- Generates a
128
-character hexadecimal (0
throughf
, lowercase) string.
- Generates a
IRandomizationSource.MailAddress()
IRandomizationSource.MailAddress(int length)
- Generates a pseudo-random
System.Net.Mail.MailAddress
. The generatedSystem.Net.Mail.MailAddress.User
will be adot-atom
form oflocal-part
(see RFC-2822 section 3.4.1). The generatedSystem.Net.Mail.MailAddress.Host
will be adomain
(see RFC-1035 section 2.3.1).
- Generates a pseudo-random
IRandomizationSource.MailAddressAddrSpec(int length)
IRandomizationSource.MailAddressAddrSpec((int LocalPartLength, int DomainLength) componentLengths)
- Generates a pseudo-random
string
according toaddr-spec
(see RFC-2822 section 3.4.1). The generatedlocal-part
will be ofdot-atom
form (see RFC-2822 section 3.4.1). The generateddomain
will be ofdot-atom
form (see RFC-2822 section 3.4.1), and its value will be generated as a RFC-1035 section 2.3.1domain
.
- Generates a pseudo-random
IRandomizationSource.DomainName(int length)
IRandomizationSource.DomainName(IReadOnlyList<int> domainLabelLengths)
- Generates a pseudo-random
string
according todomain
(see RFC-1035 section 2.3.1).
- Generates a pseudo-random
IRandomizationSource.RandomUrl(int hostLength, int pathLength = 0, int queryLength = 0, int fragmentLength = 0, string scheme = "https", int? port = null)
- Generates a pseudo-random
string
URL according to RFC-3986 URI syntax. Thehost
segment is generated usingIRandomizationSource.DomainName(int length)
.
- Generates a pseudo-random
IRandomizationSource.IpV4(byte? octet1 = null, byte? octet2 = null, byte? octet3 = null, byte? octet4 = null)
- Generates a pseudo-random IP v4 address.
IRandomizationSource.IpV6(ushort? piece1 = null, ushort? piece2 = null, ushort? piece3 = null, ushort? piece4 = null, ushort? piece5 = null, ushort? piece6 = null, ushort? piece7 = null, ushort? piece8 = null)
- Generates a pseudo-random IP v6 address.
Generate a Sequence of Items from a Markov Chain Model
IRandomizationSource.CreateMarkovGenerator(IReadOnlyCollection<IReadOnlyList<T>> sources, int chainLength = 1) where T : notnull, IEquatable<T>
- Generates a
Func<int, IReadOnlyList<T>>
which accepts anint maxLength
and uses a Markov Chain model, derived fromsources
, to generate sequences ofT
of up to lengthmaxLength
.- The
chainLength
determines how manyT
are grouped to determine Markov Chain probability.
- The
- Generates a
IRandomizationSource.CreateMarkovGenerator(IEnumerable<string> sources, int chainLength = 1)
- Generates a
Func<int, string>
which accepts anint maxLength
and uses a Markov Chain model, derived fromsources
, to generate strings of up to lengthmaxLength
.- The
chainLength
determines how many characters in each inputstring
are grouped to determine Markov Chain probability.
- The
- Example uses: create a random word or name generator using a list of example words or names.
- Example:
- Generates a
var exampleFruitNames = new[]
{
"apple", "apricot", "avocado", "banana", "blackberry", "blackcurrant", "blueberry", "boysenberry", "cantaloupe",
"caper", "cherry", "cranberry", "elderberry", "fig", "gooseberry", "grape", "grapefruit", "guava", "jujube",
"kiwi", "kumquat", "lemon", "lime", "lychee", "mango", "mulberry", "olive", "orange", "papaya", "pear",
"persimmon", "pineapple", "plantain", "plum", "pomegranate", "raspberry", "starfruit", "strawberry", "tangerine",
"watermelon"
};
Func<int, string> fruitGenerator = Randomizer.Shared.CreateMarkovGenerator(sources: exampleFruitNames, chainLength: 2);
string generatedFruit = fruitGenerator(maxLength: 12);
string possiblyLongerGeneratedFruit = fruitGenerator(maxLength: 20);
string likelyShorterGeneratedFruit = fruitGenerator(maxLength: 6);
IRandomizationSource.GenerateRandomMarkov<T>(IReadOnlyCollection<IReadOnlyList<T>> sources, int? maxLength = null, int chainLength = 1)
- Generates a
IReadOnlyList<T>
based upon a Markov Chain model, derived fromsources
.- The
chainLength
determines how many items in each inputIReadOnlyList<T>
are grouped to determine Markov Chain probability.
- The
- Use
IRandomizationSource.CreateMarkovGenerator()
(above), instead of this function, unless only a single value is needed.- The resources needed to generate the Markov Chain model are non-trivial. This function creates a new model each time it is executed, consuming both computational and memory resources.
- Generates a
IRandomizationSource.GenerateRandomMarkov(IEnumerable<string> sources, int? maxLength = null, int chainLength = 1)
- Generates a
string
based upon a Markov Chain model, derived fromsources
..- The
chainLength
determines how many characters in each inputstring
are grouped to determine Markov Chain probability.
- The
- Use
IRandomizationSource.CreateMarkovGenerator()
(above), instead of this function, unless only a single value is needed.- The resources needed to generate the Markov Chain model are non-trivial. This function creates a new model each time it is executed, consuming both computational and memory resources.
- Example uses: create a random word or name generator using a list of example words or names.
- Example:
- Generates a
var exampleFruitNames = new[]
{
"apple", "apricot", "avocado", "banana", "blackberry", "blackcurrant", "blueberry", "boysenberry", "cantaloupe",
"caper", "cherry", "cranberry", "elderberry", "fig", "gooseberry", "grape", "grapefruit", "guava", "jujube",
"kiwi", "kumquat", "lemon", "lime", "lychee", "mango", "mulberry", "olive", "orange", "papaya", "pear",
"persimmon", "pineapple", "plantain", "plum", "pomegranate", "raspberry", "starfruit", "strawberry", "tangerine",
"watermelon"
};
string similarGeneratedFruit = Randomizer.Shared.GenerateRandomMarkov(sources: exampleFruitNames, maxLength: 12, chainLength: 2);
string slightlySimilarGeneratedFruit = Randomizer.Shared.GenerateRandomMarkov(sources: exampleFruitNames, maxLength: 20, chainLength: 1);
string verySimilarGeneratedFruit = Randomizer.Shared.GenerateRandomMarkov(sources: exampleFruitNames, maxLength: 15, chainLength: 3);
Generate a Word, Sentence, or Paragraph (Lorem Ipsum)
The Lorem Ipsum random generators create Latin-like words, sentences, and paragraphs. They use Markov Chain models trained on multiple Latin sources: the traditional Lorem Ipsum excerpts of Cicero's De Finibus Bonorum et Malorum, and excerpts of René Descartes's Meditationes de Prima Philosophia.
IRandomizationSource.LoremIpsumParagraph((int WordCount, int MaxWordLength) paragraphParameters)
- Generates a paragraph
string
ofparagraphParameters.WordCount
words, broken into a random number of sentences. Each word in the paragraph is no more thanparagraphParameters.MaxWordLength
characters.
- Generates a paragraph
IRandomizationSource.LoremIpsumSentence((int WordCount, int MaxWordLength) sentenceParameters)
- Generates a sentence
string
ofsentenceParameters.WordCount
words, each word no more thansentenceParameters.MaxWordLength
characters. Sentences always begin with a capital letter and end with a period (.
).
- Generates a sentence
IRandomizationSource.LoremIpsumWord(int maxLength)
- Generates a word
string
of no more thanmaxLength
characters.
- Generates a word
The name generators use Markov Chain models trained on public census and government data.
IRandomizationSource.DemographicsBirthDateTime(DateTime? relativeTo = null)
IRandomizationSource.DemographicsBirthDateTime((int MinAgeInYears, int MaxAgeInYearsExclusive) ageRange, DateTime? relativeTo = null)
- Generates a date of birth
DateTime
within theageRange
specified, relative torelativeTo
. If not provided,ageRange
defaults to(MinAgeInYears: 18, MaxAgeInYearsExclusive: 96)
. If not provided,relativeTo
defaults toDateTime.UtcNow
.
- Generates a date of birth
IRandomizationSource.DemographicsForenameUsa(int maxLength)
- Generates a forename
string
of no more thanmaxLength
characters. Generated names have an initial capital letter and all subsequent characters are lowercase.
- Generates a forename
IRandomizationSource.DemographicsSurnameUsa(int maxLength)
- Generates a surname
string
of no more thanmaxLength
characters. Generated names have an initial capital letter and all subsequent characters are lowercase.
- Generates a surname
This library does not provide a domain-specific language. However, by creating your own static
methods extending IStatefulRandomizationSource<TState>
(where TState
should be your generator options), you can easily construct a domain-specific language to generate complex or custom types.
This library's unit tests include an example Domain-Specific Language which generates characters for a role-playing game. The documented example illustrates:
- creating a randomization state object (to provide configuration)
- creating a domain value object (the thing we want to generate)
- creating a domain-specific language (a
static
method extendingIStatefulRandomizationSource<TState>
which used the configuration and generated the value object)- creating unit tests which use the domain-specific language
This functionality was already possible by extending IRandomizationSource
with methods accepting parameters. However, IStatefulRandomizationSource<TState>
provides greater flexibility. By embedding state object types into your extension methods, you can simplify verbose generator method signatures.
The IStatefulRandomizationSource<TState>
extends IRandomizationSource
with a public TState State { get; }
property.
Use Randomizer.WithState()
static methods to create a new stateful randomizer based upon an initial state value.
Use the .WithState(TState initialState)
extension method on IRandomizationSource
to create a derived stateful randomizer using that randomization source instance.
In some scenarios, you may want to modify the existing randomization source state (e.g., when designing a "builder" interface). This is handled using some monadic methods.
It is intended that the state values used in a randomization source are immutable. When the state needs to change, the following monadic methods allow you to derive a new
IStatefulRandomizationSource<TState>
using the current state value.
IStatefulRandomizationSource<TStateCurrent,TStateNew>.Bind(Func<TStateCurrent,IStatefulRandomizationSource<TStateNew>>)
- Use
Bind
to replace the stateful randomization source with a new stateful randomization source derived from current state. This is an uncommonIStatefulRandomizationSource
operation, normally only used when swapping the underlyingIRandomizationSource
.
- Use
IStatefulRandomizationSource<TStateCurrent,TStateNew>.Map(Func<TStateCurrent,TStateNew>)
- Use
Map
to replace the state contained within the stateful randomization source with a new state (which may be of the same type or another). This is the most common state operation.
- Use