Testy 0.10.0
dotnet add package Testy --version 0.10.0
NuGet\Install-Package Testy -Version 0.10.0
<PackageReference Include="Testy" Version="0.10.0" />
paket add Testy --version 0.10.0
#r "nuget: Testy, 0.10.0"
// Install Testy as a Cake Addin #addin nuget:?package=Testy&version=0.10.0 // Install Testy as a Cake Tool #tool nuget:?package=Testy&version=0.10.0
Testy
Just some nifites (dependent on NUnit 3) to help with testing things.
It's a highly personal collection, so it's probably not useful for that many people.
What it can do
Fixture base
First of all, there's a FixtureBase
with a stack of IDisposable
s in it, enabling this pattern
via the generic Using
method:
// anywhere in your test fixture derived from TestFixture:
var disposable = Using(new SomethingDisposable());
// disposable is now tracked and will be disposed during the tear-down phase
The disposables are disposed in the reverse order of which they were registered, which is natural, because it's a stack.
Temporary test directory
When testing thing in the file system, it's often useful to have easy access to a clean test directory, which gets automatically cleaned up after the test.
That's what TemporaryTestDirectory
does - you can use it like this:
var dir = Using(new TemporaryTestDirectory());
// pass dir to something that needs it (will implicitly cast itself to a string)
var sut = new SomethingThatNeedsThisDirectory(dir);
Getting stuff in random order
When you have lists of things that you feed to your system under test, you sometimes want to feed it the list in random order.
That's easy, because there's an InRandomOrder
extension for IEnumerable<T>
that does just that:
var names = new[] {
"Bob",
"Pop",
"Top",
"Knoppers"
};
ProcessNames(names.InRandomOrder());
// they're processed in random order!!
Getting some random items from a list
For test randomization, it can be useful to introduce variation into values used for testing. If you have a list of things
var things = new[] { 1, 2, 3, 4, 5 };
then you may generate a new list of items consisting of n randomly picked items from that list by going
var randomThings = 10.RandomPicksFrom(things);
where randomThings
will now be 10 long and contain something like [2, 2, 1, 5, 4, 5, 5, 2, 3, 1]
.
Cancellation
When you're in a test fixture derived from FixtureBase
, you can get a new CancellationToken
set to
be cancelled after a set timeout by calling CancelAfter
, e.g. like this:
[Test]
public async Task TestSomethingAsync()
{
var result = await CallSomethingAsync(cancellationToken: CancelAfter(delay: TimeSpan.FromSeconds(3)));
Assert.That(result, Is.Not.Null);
}
Disposable callback
If you want to "do something" when your test is over, but you don't know yet what it is (or maybe it's not
disposable in itself), you can take advantage of the DisposableCallback
:
SqlConnection _connection;
SqlTransaction _transaction;
protected override void SetUp()
{
_connection = InitializeSqlConnection();
_transaction = _connection.BeginTransaction();
// be sure to roll back the transaction after the test:
Using(new DisposableCallback(() => _transaction.RollBack()));
// now use _transaction throughout the tests
}
Simple benchmarking
If you want to measure the time something takes, and possible calculate a rate (e.g. something like "number of whatevers per second") along with that,
then Testy provides a TimerScope
that can do that. In your test fixture derived from FixtureBase
, you can simply go
using (TimerScope("Process one billion whatevers"))
{
// do stuff that takes time in here
}
and then the timing information will be printed like this after the test:
SCOPE 'Process one billion whatevers' completed in 450.2332 ms
If you want to calculate a rate along with that, pass it in as the optional countForRateCalculation
parameter like this:
using (TimerScope("Handle 10000 messages", countForRateCalculation: 10000))
{
// process the 10000 messages in here
}
and then the output will read
SCOPE 'Handle 10000 messages' completed in 550.43 ms | 0.055043 ms/item | 18.16761441055175 items/ms
If you're not in the test fixture, or your test fixture is not derived from FixtureBase
, you can just new up the timer scope:
using (new TimerScope(...))
{
// no problemo!
}
Fun with concurrent queues
If you're writing tests that put stuff in a ConcurrentQueue<T>
, and you want to e.g. wait for the queue to contain a number of items,
then Testy provides a WaitOrDie(...)
extension to help you with that. Check this out � this will wait for the queue to contain at least 100 items:
await queue.WaitOrDie(q => q.Count >= 100);
Often you want an exact number of items � say 100 � and you want it to fail, if it exceeds that number. This code accomplishes that:
await queue.WaitOrDie(q => q.Count == 100, failExpression: q => q.Count > 100);
If case of failure, if you want the thrown exception to contain additional details about what might have caused the failure, you can
provide a failureDetailsFunction
:
await queue.WaitOrDie(
q => q.Count == 100,
failExpression: q => q.Count > 100,
failureDetailsFunction: () => $@"Here's the queue contents: {string.Join(", ", queue)}"
);
If you're not happy with the default 5 s timeout, do this:
await queue.WaitOrDie(q => q.Count >= 100, timeoutSeconds: 60); // take it easy
Periodically doing something
When you're writing tests that process stuff asynchronously, you often want to periodically print out information on how that goes.
E.g. if your asynchronous SUT puts things in a ConcurrentQueue<string>
, and you wait for the queue to contain 100 items, you might
be waiting by using the aforementioned WaitOrDie(...)
function:
await queue.WaitOrDie(q => q.Count == 100, failExpression: q => q.Count > 100);
How about we print out how many items are in the queue every second, while running the test? Easy! That can be done like this:
using (PeriodicCallback(TimeSpan.FromSeconds(1), () => Console.WriteLine($"Count: {queue.Count}")))
{
await queue.WaitOrDie(q => q.Count == 100, failExpression: q => q.Count > 100);
}
If you're not in the test fixture, or your test fixture is not derived from FixtureBase
, you can just new up the periodic callback:
using (new PeriodicCallback(...))
{
// no problemo!
}
Fun with JSON
Testy has extensions that makes a few JSON tricks readily available to your tests. E.g. you can easily dump an object to the console like this:
Console.WriteLine(obj.ToJson());
provided, of course, that the object is JSON serializable. If the default output is too condensed, you can
Console.WriteLine(obj.ToPrettyJson());
to indent it nicely.
The ToPrettyJson
method above will automatically detect whether the input is a JSON string, in which case
it will deserialize it and return it indented too.
If you're in doubt whether a string str
is valid JSON, you can check it like this:
Assert.That(str.IsJson(), Is.True, $"Expected {str} to contain valid JSON");
If you just want to quickly print an object as JSON to the console, you can just
object.DumpJson();
and then that's exactly what happens.
Fun with tables
Let's say you have a sequence of objects like this:
var objects = new[]
{
new {FirstColumn = "r1", SecondColumn = "hej", ThirdColumn = "hej igen"},
new {FirstColumn = "r2", SecondColumn = "hej", ThirdColumn = "hej igen"},
};
You can get them in tabular form like this:
Console.WriteLine($@"Got the following objects:
{objects.ToTable()}");
which will render nicely like this:
Got the following objects:
+-------------+--------------+-------------+
| FirstColumn | SecondColumn | ThirdColumn |
+-------------+--------------+-------------+
| r1 | hej | hej igen |
+-------------+--------------+-------------+
| r2 | hej | hej igen |
+-------------+--------------+-------------+
If you're lazy, and you just want to output that tabular data to the console right away, you can just go
objects.DumpTable();
and
+-------------+--------------+-------------+
| FirstColumn | SecondColumn | ThirdColumn |
+-------------+--------------+-------------+
| r1 | hej | hej igen |
+-------------+--------------+-------------+
| r2 | hej | hej igen |
+-------------+--------------+-------------+
will be printed.
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 is compatible. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
.NET Framework | net462 is compatible. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
-
.NETFramework 4.6.2
- newtonsoft.json (>= 13.0.3)
- NUnit (>= 4.1.0)
- System.ValueTuple (>= 4.5.0)
- tababular (>= 4.3.0)
-
net6.0
- newtonsoft.json (>= 13.0.3)
- NUnit (>= 4.1.0)
- System.ValueTuple (>= 4.5.0)
- tababular (>= 4.3.0)
-
net7.0
- newtonsoft.json (>= 13.0.3)
- NUnit (>= 4.1.0)
- System.ValueTuple (>= 4.5.0)
- tababular (>= 4.3.0)
-
net8.0
- newtonsoft.json (>= 13.0.3)
- NUnit (>= 4.1.0)
- System.ValueTuple (>= 4.5.0)
- tababular (>= 4.3.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated |
---|---|---|
0.10.0 | 990 | 8/21/2024 |
0.9.0 | 3,602 | 7/26/2023 |
0.8.0 | 11,174 | 6/30/2022 |
0.7.0 | 1,595 | 2/14/2022 |
0.6.0 | 462 | 2/13/2022 |
0.5.0 | 447 | 2/13/2022 |
0.4.0 | 367 | 1/10/2022 |
0.3.0 | 4,959 | 1/19/2021 |
0.2.0 | 3,944 | 1/8/2020 |
0.1.0 | 3,418 | 10/29/2019 |
0.0.12 | 609 | 10/25/2019 |
0.0.11 | 628 | 10/25/2019 |
0.0.10 | 672 | 10/25/2019 |
0.0.9 | 677 | 10/25/2019 |
0.0.8 | 650 | 10/13/2019 |
0.0.7 | 1,749 | 4/25/2019 |
0.0.6 | 729 | 4/25/2019 |
0.0.5 | 769 | 4/21/2019 |
0.0.4 | 1,085 | 5/23/2018 |