CsCheck alternatives and similar packages
Based on the "Testing" category.
Alternatively, view CsCheck alternatives based on common mentions on social networks and blogs.
-
Bogus
:card_index: A simple fake data generator for C#, F#, and VB.NET. Based on and ported from the famed faker.js. -
Fluent Assertions
A very extensive set of extension methods that allow you to more naturally specify the expected outcome of a TDD or BDD-style unit tests. Targets .NET Framework 4.7, as well as .NET Core 2.1, .NET Core 3.0, .NET 6, .NET Standard 2.0 and 2.1. Supports the unit test frameworks MSTest2, NUnit3, XUnit2, MSpec, and NSpec3. -
Testcontainers
A library to support tests with throwaway instances of Docker containers for all compatible .NET Standard versions. -
AutoFixture
AutoFixture is an open source library for .NET designed to minimize the 'Arrange' phase of your unit tests in order to maximize maintainability. Its primary goal is to allow developers to focus on what is being tested rather than how to setup the test scenario, by making it easier to create object graphs containing test data. -
Verify
Verify is a snapshot testing tool that simplifies the assertion of complex data models and documents. -
NBomber
Distributed load-testing framework for .NET. Create distributed load test scenarios entirely using plain C# or F#. It is designed to test any system regardless of the protocol or a semantic model (Pull/Push). -
WireMock.Net
WireMock.Net is a flexible product for stubbing and mocking web HTTP responses using advanced request matching and response templating. Based on the functionality from http://WireMock.org, but extended with more functionality. -
Compare-Net-Objects
What you have been waiting for :+1: Perform a deep compare of any two .NET objects using reflection. Shows the differences between the two objects. -
MSTest
This repository holds the source code of Microsoft.Testing.Platform (MTP), a lightweight alternative to VSTest, as well as MSTest adapter and framework. -
Machine.Specifications
Machine.Specifications is a Context/Specification framework for .NET that removes language noise and simplifies tests. -
ArchUnitNET
A C# architecture test library to specify and assert architecture rules in C# for automated testing. -
GenFu
GenFu is a library you can use to generate realistic test data. It is composed of several property fillers that can populate commonly named properties through reflection using an internal database of values or randomly created data. You can override any of the fillers, give GenFu hints on how to fill them. -
Expecto
A smooth testing lib for F#. APIs made for humans! Strong testing methodologies for everyone! -
NBuilder
DISCONTINUED. Rapid generation of test objects in .NET [Moved to: https://github.com/nbuilder/nbuilder] -
Fine Code Coverage
Visualize unit test code coverage easily for free in Visual Studio Community Edition (and other editions too) -
NFluent
Smooth your .NET TDD experience with NFluent! NFluent is an ergonomic assertion library which aims to fluent your .NET TDD experience (based on simple Check.That() assertion statements). NFluent aims your tests to be fluent to write (with a super-duper-happy 'dot' auto-completion experience), fluent to read (i.e. as close as possible to plain English expression), but also fluent to troubleshoot, in a less-error-prone way comparing to the classical .NET test frameworks. NFluent is also directly inspired by the awesome Java FEST Fluent assertion/reflection library (http://fest.easytesting.org/) -
xBehave.net
DISCONTINUED. ✖ An xUnit.net extension for describing each step in a test with natural language. -
SpecsFor
SpecsFor is a light-weight Behavior-Driven Development framework that focuses on ease of use for *developers* by minimizing testing friction. -
Xunit.Gherkin.Quick
BDD in .NET Core - using Xunit and Gherkin (compatible with both .NET Core and .NET) -
Moq.Contrib.HttpClient
A set of extension methods for mocking HttpClient and IHttpClientFactory with Moq. -
SimpleStubs
DISCONTINUED. *SimpleStubs* is a simple mocking framework that supports Universal Windows Platform (UWP), .NET Core and .NET framework. SimpleStubs is currently developed and maintained by Microsoft BigPark Studios in Vancouver. -
#<Sawyer::Resource:0x00007f89811b9240>
:fire: A small library to help .NET developers leverage Microsoft's dependency injection framework in their Xunit-powered test projects -
SecTester
SecTester is a new tool that integrates our enterprise-grade scan engine directly into your unit tests.
InfluxDB high-performance time series database

* Code Quality Rankings and insights are calculated and provided by Lumnify.
They vary from L1 to L5 with "L5" being the highest.
Do you think we are missing an alternative of CsCheck or a related project?
README
CsCheck
CsCheck is a C# random testing library inspired by QuickCheck.
It differs in that generation and shrinking are both based on PCG, a fast random number generator.
This gives the following advantages:
- Automatic shrinking. Gen classes are composable with no need for Arb classes. So less boilerplate.
- Random testing and shrinking are parallelized. This and PCG make it very fast.
- Shrunk cases have a seed value. Simpler examples can easily be reproduced.
- Shrinking can be continued later to give simpler cases for high dimensional problems.
- Concurrency testing and random shrinking work well together. Repeat is not needed.
See why you should use it, the comparison with other random testing libraries, or how CsCheck does in the shrinking challenge. In one shrinking challenge test CsCheck managed to shrink to a new smaller example than was thought possible and is not reached by any other testing library.
CsCheck also has functionality to make multiple types of testing simple and fast:
- Random testing
- Model-based testing
- Metamorphic testing
- Concurrency testing
- Causal profiling
- Regression testing
- Performance testing
- Debug utilities
- Configuration
The following tests are in xUnit but could equally be used in any testing framework.
More to see in the [Tests](Tests). There are also 1,000+ F# tests using CsCheck in MKL.NET.
No Reflection was used in the making of this product.
Random testing
Unit Single
[Fact]
public void Single_Unit_Range()
{
Gen.Single.Unit.Sample(f => Assert.InRange(f, 0f, 0.9999999f));
}
Long Range
[Fact]
public void Long_Range()
{
(from t in Gen.Select(Gen.Long, Gen.Long)
let start = Math.Min(t.V0, t.V1)
let finish = Math.Max(t.V0, t.V1)
from value in Gen.Long[start, finish]
select (value, start, finish))
.Sample(i => Assert.InRange(i.value, i.start, i.finish));
}
Int Distribution
[Fact]
public void Int_Distribution()
{
int buckets = 70;
int frequency = 10;
int[] expected = ArrayRepeat(buckets, frequency);
Gen.Int[0, buckets - 1].Array[frequency * buckets]
.Select(sample => Tally(buckets, sample))
.SampleOne(actual => Check.ChiSquared(expected, actual));
}
Serialization Roundtrip
static void TestRoundtrip<T>(Gen<T> gen, Action<Stream, T> serialize, Func<Stream, T> deserialize)
{
gen.Sample(t =>
{
using var ms = new MemoryStream();
serialize(ms, t);
ms.Position = 0;
return deserialize(ms).Equals(t);
});
}
[Fact]
public void Varint()
{
TestRoundtrip(Gen.UInt, StreamSerializer.WriteVarint, StreamSerializer.ReadVarint);
}
[Fact]
public void Double()
{
TestRoundtrip(Gen.Double, StreamSerializer.WriteDouble, StreamSerializer.ReadDouble);
}
[Fact]
public void DateTime()
{
TestRoundtrip(Gen.DateTime, StreamSerializer.WriteDateTime, StreamSerializer.ReadDateTime);
}
Shrinking Challenge
[Fact]
public void No2_LargeUnionList()
{
Gen.Int.Array.Array
.Sample(aa =>
{
var hs = new HashSet<int>();
foreach (var a in aa)
{
foreach (var i in a) hs.Add(i);
if (hs.Count >= 5) return false;
}
return true;
});
}
Recursive
record MyObj(int Id, MyObj[] Children);
[Fact]
public void RecursiveDepth()
{
int maxDepth = 4;
Gen.Recursive<MyObj>((i, my) =>
Gen.Select(Gen.Int, my.Array[0, i < maxDepth ? 6 : 0], (i, a) => new MyObj(i, a))
)
.Sample(i =>
{
static int Depth(MyObj o) => o.Children.Length == 0 ? 0 : 1 + o.Children.Max(Depth);
return Depth(i) <= maxDepth;
});
}
Model-based testing
Model-based is the most efficient form of random testing. Only a small amount of code is needed to fully test functionality. SampleModelBased generates an initial actual and model and then applies a random sequence of operations to both checking that the actual and model are still equal.
SetSlim Add
[Fact]
public void SetSlim_ModelBased()
{
Gen.Int.Array.Select(a => (new SetSlim<int>(a), new HashSet<int>(a)))
.SampleModelBased(
Gen.Int.Operation<SetSlim<int>, HashSet<int>>((ls, l, i) =>
{
ls.Add(i);
l.Add(i);
})
// ... other operations
);
}
Metamorphic testing
The second most efficient form of random testing is metamorphic which means doing something two different ways and checking they produce the same result. SampleMetamorphic generates two identical initial samples and then applies the two functions and asserts the results are equal. This can be needed when no model can be found that is not just a reimplementation.
More about how useful metamorphic tests can be here: How to specify it!.
MapSlim Update
[Fact]
public void MapSlim_Metamorphic()
{
Gen.Dictionary(Gen.Int, Gen.Byte)
.Select(d => new MapSlim<int, byte>(d))
.SampleMetamorphic(
Gen.Select(Gen.Int[0, 100], Gen.Byte, Gen.Int[0, 100], Gen.Byte).Metamorphic<MapSlim<int, byte>>(
(d, t) => { d[t.V0] = t.V1; d[t.V2] = t.V3; },
(d, t) => { if (t.V0 == t.V2) d[t.V2] = t.V3; else { d[t.V2] = t.V3; d[t.V0] = t.V1; } }
)
);
}
Concurrency testing
CsCheck has support for concurrency testing with full shrinking capability. A concurrent sequence of operations are run on an initial state and the result is compared to all the possible linearized versions. At least one of these must be equal to the concurrent version.
Idea from John Hughes talk and paper. This is actually easier to implement with CsCheck than QuickCheck because the random shrinking does not need to repeat each step as QuickCheck does (10 times by default) to make shrinking deterministic.
SetSlim
[Fact]
public void SetSlim_Concurrency()
{
Gen.Byte.Array.Select(a => new SetSlim<byte>(a))
.SampleConcurrent(
Gen.Byte.Operation<SetSlim<byte>>((l, i) => { lock (l) l.Add(i); }),
Gen.Int.NonNegative.Operation<SetSlim<byte>>((l, i) => { if (i < l.Count) { var _ = l[i]; } }),
Gen.Byte.Operation<SetSlim<byte>>((l, i) => { var _ = l.IndexOf(i); }),
Gen.Operation<SetSlim<byte>>(l => l.ToArray())
);
}
Causal profiling
Causal profiling is a technique to investigate the effect of speeding up one or more concurrent regions of code. It shows which regions are the bottleneck and what overall performance gain could be achieved from each region.
Idea from Emery Berger. My blog posts on this here.
Fasta
[Fact]
public void Fasta()
{
Causal.Profile(() => FastaUtils.Fasta.NotMain(10_000_000, null)).Output(writeLine);
}
static int[] Rnds(int i, int j, ref int seed)
{
var region = Causal.RegionStart("rnds");
var a = intPool.Rent(BlockSize1);
var s = a.AsSpan(0, i);
s[0] = j;
for (i = 1, j = Width; i < s.Length; i++)
{
if (j-- == 0)
{
j = Width;
s[i] = IM * 3 / 2;
}
else
{
s[i] = seed = (seed * IA + IC) % IM;
}
}
Causal.RegionEnd(region);
return a;
}
Regression testing
Portfolio Calculation
Example is used to find, pin and continue to check a suitable generated example e.g. to cover a certain codepath.
Hash is used to find and check a hash for a number of results.
It saves a cache of the results on a successful hash check and each subsequent run will fail with actual vs expected at the first point of any difference.
Together Example and Hash eliminate the need to commit data files in regression testing while also giving detailed information of any change.
[Fact]
public void Portfolio_Small_Mixed_Example()
{
var portfolio = ModelGen.Portfolio.Example(p =>
p.Positions.Count == 5
&& p.Positions.Any(p => p.Instrument is Bond)
&& p.Positions.Any(p => p.Instrument is Equity)
, "0N0XIzNsQ0O2");
var currencies = portfolio.Positions.Select(p => p.Instrument.Currency).Distinct().ToArray();
var fxRates = ModelGen.Price.Array[currencies.Length].Example(a =>
a.All(p => pp is > 0.75 and < 1.5)
, "ftXKwKhS6ec4");
double fxRate(Currency c) => fxRates[Array.IndexOf(currencies, c)];
Check.Hash(h =>
{
h.Add(portfolio.Positions.Select(p => p.Profit));
h.Add(portfolio.Profit(fxRate));
h.Add(portfolio.RiskByPosition(fxRate));
}, 5857230471108592669, decimalPlaces: 2);
}
Performance testing
Linq Sum
[Fact]
public void Faster_Linq_Random()
{
Gen.Byte.Array[100, 1000]
.Faster(
data => data.Aggregate(0.0, (t, b) => t + b),
data => data.Select(i => (double)i).Sum()
)
.Output(writeLine);
}
The performance is raised in an exception if it fails but can also be output if it passes with the above output function.
Tests.CheckTests.Faster_Linq_Random [27ms]
Standard Output Messages:
32.2%[29.4%..36.5%] faster, sigma=50.0 (2,551 vs 17)
The first number is the estimated median performance improvement with the interquartile range in the square brackets. The counts of faster vs slower and the corresponding sigma (the number of standard deviations of the binomial distribution for the null hypothosis P(faster) = P(slower) = 0.5) are also shown. The default sigma used is 6.0.
Matrix Multiply
[Fact]
public void Faster_Matrix_Multiply_Range()
{
var genDim = Gen.Int[5, 30];
var genArray = Gen.Double.Unit.Array2D;
Gen.SelectMany(genDim, genDim, genDim, (i, j, k) => Gen.Select(genArray[i, j], genArray[j, k]))
.Faster(
MulIKJ,
MulIJK
);
}
MapSlim Increment
[Fact]
public void MapSlim_Performance_Increment()
{
Gen.Byte.Array
.Select(a => (a, new MapSlim<byte, int>(), new Dictionary<int, int>()))
.Faster(
(items, mapslim, _) =>
{
foreach (var b in items)
mapslim.GetValueOrNullRef(b)++;
},
(items, _, dict) =>
{
foreach (var b in items)
{
dict.TryGetValue(b, out int c);
dict[b] = c + 1;
}
},
repeat: 100
).Output(writeLine);
}
Tests.SlimCollectionsTests.MapSlim_Performance_Increment [27 s]
Standard Output Messages:
66.0%[56.4%..74.8%] faster, sigma=200.0 (72,690 vs 13,853)
Benchmarks Game
[Fact]
public void ReverseComplement_Faster()
{
if (!File.Exists(Utils.Fasta.Filename)) Utils.Fasta.NotMain(new[] { "25000000" });
Check.Faster(
ReverseComplementNew.RevComp.NotMain,
ReverseComplementOld.RevComp.NotMain,
threads: 1, timeout: 600_000, sigma: 6
)
.Output(writeLine);
}
Tests.ReverseComplementTests.ReverseComplement_Faster [27s 870ms]
Standard Output Messages:
25.1%[20.5%..31.6%] faster, sigma=6.0 (36 vs 0)
Varint
Repeat is used as the functions are very quick.
[Fact]
public void Varint_Faster()
{
Gen.Select(Gen.UInt, Gen.Const(() => new byte[8]))
.Faster(
(i, bytes) =>
{
int pos = 0;
ArraySerializer.WriteVarint(bytes, ref pos, i);
pos = 0;
return ArraySerializer.ReadVarint(bytes, ref pos);
},
(i, bytes) =>
{
int pos = 0;
ArraySerializer.WritePrefixVarint(bytes, ref pos, i);
pos = 0;
return ArraySerializer.ReadPrefixVarint(bytes, ref pos);
}, sigma: 10, repeat: 200)
.Output(writeLine);
}
Tests.ArraySerializerTests.Varint_Faster [45 ms]
Standard Output Messages:
10.9%[-3.2%..25.8%] faster, sigma=10.0 (442 vs 190)
Debug utilities
The Dbg module is a set of utilities to collect, count and output debug info, time, classify generators, define and remotely call functions, and perform in code regression during testing. CsCheck can temporarily be added as a reference to run in non test code. Note this module is only for temporary debug use and the API may change between minor versions.
Count, Info, Set, Get, CallAdd, Call
public void Normal_Code(int z)
{
Dbg.Count();
var d = Calc1(z).DbgSet("d");
Dbg.Call("helpful");
var c = Calc2(d).DbgInfo("c");
Dbg.CallAdd("test cache", () =>
{
Dbg.Info(Dbg.Get("d"));
Dbg.Info(cacheItems);
});
}
[Fact]
public void Test()
{
Dbg.CallAdd("helpful", () =>
{
var d = (double)Dbg.Get("d");
// ...
Dbg.Set("d", d);
});
Normal_Code(z);
Dbg.Call("test cache");
Dbg.Output(writeLine);
}
Classify
[Fact]
public void Test()
{
Gen.Int.Array
.DbgClassify(a => a.Length switch
{
0 => "zero length",
1 => "single length",
< 10 => "less ten",
> 100 => "over 100",
_ => "10 - 100",
})
.Sample(a =>
{
// ...
});
Dbg.Output(writeLine);
}
Regression
public double[] Calculation(InputData input)
{
var part1 = CalcPart1(input);
// Add items to the regression on first pass, throw/break here if different on subsequent.
Dbg.Regression.Add(part1);
var part2 = CalcPart2(part1).DbgTee(Dbg.Regression.Add); // Tee can be used to do this inline.
// ...
return CalcFinal(partN).DbgTee(Dbg.Regression.Add);
}
[Fact]
public void Test()
{
// Remove any previously saved regression data.
Dbg.Regression.Delete();
Calculation(InputSource1());
// End first pass save mode (only needed if second pass is in this process run).
Dbg.Regression.Close();
// Subsequent pass could be now or a code change and rerun (without the Delete).
Calculation(InputSource2());
// Check full number of items have been reconciled (optional).
Dbg.Regression.Close();
}
Time
public Result CalcPart2(InputData input)
{
using var time = Dbg.Time();
// Calc
time.Line();
// Calc more
time.Line();
// ...
return result;
}
public void LongProcess()
{
using var time = Dbg.Time();
var part1 = CalcPart1(input);
time.Line();
var part2 = new List<Result>();
foreach(var item in part1)
part2.Add(CalcPart2(item));
time.Line();
// ...
return CalcFinal(partN);
}
[Fact]
public void Test()
{
LongProcess();
Dbg.Output(writeLine);
}
Configuration
Check functions accept configuration optional parameters e.g. iter: 100_000, seed: "0N0XIzNsQ0O2", print: t => string.Join(", ", t):
iter - The number of iterations to run in the sample (default 100).
time - The number of seconds to run the sample.
seed - The seed to use for the first iteration.
threads - The number of threads to run the sample on (default number logical CPUs).
timeout - The timeout in seconds to use for Faster (default 60 seconds).
print - A function to convert the state to a string for error reporting (default Check.Print).
equal - A function to check if the two states are the same (default Check.Equal).
sigma - For Faster sigma is the number of standard deviations from the null hypothosis (default 6).
replay - The number of times to retry the seed to reproduce a SampleConcurrent fail (default 100).
Global defaults can also be set via environment variables:
$env:CsCheck_Iter = 10000; dotnet test -c Release --filter Multithreading; rm env:CsCheck*
$env:CsCheck_Time = 60; dotnet test -c Release --filter Multithreading; rm env:CsCheck*
$env:CsCheck_Seed = '0N0XIzNsQ0O2'; dotnet test -c Release --filter List; rm env:CsCheck*
$env:CsCheck_Sigma = 50; dotnet test -c Release -l 'console;verbosity=detailed' --filter Faster; rm env:CsCheck*
$env:CsCheck_Threads = 1; dotnet test -c Release -l 'console;verbosity=detailed' --filter Perf; rm env:CsCheck*