LeapingGorilla.Testing.XUnit
7.1.0
Prefix Reserved
dotnet add package LeapingGorilla.Testing.XUnit --version 7.1.0
NuGet\Install-Package LeapingGorilla.Testing.XUnit -Version 7.1.0
<PackageReference Include="LeapingGorilla.Testing.XUnit" Version="7.1.0" />
<PackageVersion Include="LeapingGorilla.Testing.XUnit" Version="7.1.0" />
<PackageReference Include="LeapingGorilla.Testing.XUnit" />
paket add LeapingGorilla.Testing.XUnit --version 7.1.0
#r "nuget: LeapingGorilla.Testing.XUnit, 7.1.0"
#:package LeapingGorilla.Testing.XUnit@7.1.0
#addin nuget:?package=LeapingGorilla.Testing.XUnit&version=7.1.0
#tool nuget:?package=LeapingGorilla.Testing.XUnit&version=7.1.0
What is it
LeapingGorilla.Testing is an attribute based framework for BDD style Given/When/Then unit testing without the ceremony.
Getting It
The LeapingGorilla.Testing is now available on NuGet. Get it using:
PM> install-package LeapingGorilla.Testing
or if you prefer XUnit:
PM> install-package LeapingGorilla.Testing.XUnit
Philosophy
We at Leaping Gorilla strive to remove as much friction as possible from our testing methodology. To that end we wanted a drop dead simple way to create unit tests that adhered to a few core principles:
- Must support constructor-based dependency injection
- Must have the ability to automatically mock any dependencies we wish to use with our item under test
- Must have the ability to override automatic mocking should the need arise
- Must support a clean syntax for stubbing method calls on our dependencies
- Must have a clean BDD style Given/When/Then flow
From these needs LeapingGorilla.Testing was born.
Technology
LeapingGorilla.Testing builds on the shoulders of giants. We use NUnit as our core testing framework, NSubstitute performs mocking duties and Fast-member provides quick reflective access to members on our classes. We also have a version which builds on XUnit rather than NUnit should that be your favoured framework.
Enough of the introductions, lets look at how it works.
Quick Sample
public class WhenTestingStubbing : WhenTestingTheBehaviourOf
{
[ItemUnderTest]
public ClassRaisingAnEvent ClassRaisingEvent { get; set; }
[Dependency]
public IMockEventRaiser EventRaiser { get; set; }
private string _severeResponse;
private string _severeReturn;
[Given]
protected void TheEventRaiserHasSevereResponse()
{
_severeResponse = "This is a severe response";
EventRaiser.RaiseEvent(true).Returns(_severeResponse);
}
[When]
protected void TheEventRaiserRaisesASevereEvent()
{
_severeReturn = ClassRaisingEvent.DoSomethingWithTheEventRaiser(true);
}
[Then]
public void SevereReturnShouldMatchResponse()
{
Assert.That(_severeReturn, Is.EqualTo(_severeResponse));
}
[Then]
public void EventRaiserShouldBeCalled()
{
EventRaiser.Received(1).RaiseEvent(Arg.Any<bool>());
}
}
Here you can see the attribute led style that LeapingGorilla.Testing uses to make testing painless. We start with the concept of an ItemUnderTest
. This is the concrete instance that the framework will create for us. This concrete instance has dependencies injected via the constructor - in this case an IMockEventRaiser
. We mark a property of this with a DependencyAttribute
and we're good to go.
LeapingGorilla.Testing starts by locating all of our Dependencies, creating a mock of each and loading them into the test class. Next it finds the item under test, finds the constructor that best matches our dependencies and then creates an instance of the item under test passing the dependencies into the constructor and loads it into the test class.
From here we look for any methods marked with a GivenAttribute
. You can have as many Given methods as you want (zero, one or more). LeapingGorilla.Testing will find and execute each in turn. The order it will do so is undefined so if you need to be specific on the order that we call them use the optional Order property like:
[Given(Order = 1)]
You should use your Given
methods to setup your stubbing in dependencies. We leverage NSubstitute for this so take a look at the NSubstitute website. With our given methods executed the framework then finds your optional When
method. You may have zero or one method marked with a WhenAttribute
. If one is found it will be called and at this point we hand over to the NUnit framework to assert each of yor test cases.
NUnit will find all public methods marked with a ThenAttribute
and run each of them as an individual test. It's best practice to avoid modifying any state in your Then
tests - you can't guarantee the order they will run in so it's easy to trip yourself up.
My First Test
- Create a new test class inheriting from
WhenTestingTheBehaviourOf
- Add a property to the class you want to test, mark it with an
ItemUnderTest
attribute - If your item under test has any dependencies to inject in the constructor, add a property for each with a
Dependency
attribute. - If you need to do any test setup (stubbing dependency methods, preparing input parameters or expected output parameters) create a method to do so and mark it with a
Given
attribute. To make life easier you can split your setup into multipleGiven
methods for readability. - If your test needs to call any functionality on your item under test create a method to do so and mark it with a
When
attribute. - Create as many public void methods as necessary to assert that your item under test has performed as expected. Mark each with a
Then
attribute and add whatAssert
statements you need.
FAQ
Q: How do I provide my own dependencies?
A: Override the CreateManualDependencies
method and assign them from there
Q: How can I mock an item that I need for my test but is not a dependency?
A: Mark it with a Mock
attribute
Q: Why can I only have a single When
method?
A: Convention. At Leaping Gorilla we stick to the rule that each test class tests a single behaviour. If a behaviour can't be expressed as a single "When I X" then we take it as a sign that our code probably needs refactoring.
Q: How do I control the ordering of my Given
methods?
A: Use the optional Order
property at the point of decoration like:
[Given(Order=2)]
Q: How do I mock a dependency that is a concrete class?
A: The short answer is: You can't. The longer answer is: you can override the CreateManualDependencies
method and substitute your own mock object there but we cannot generate an automatic mock. This is down to the nature of the .Net framework and short of using an expensive tool like TypeMock or JustMock itisn't going to change. Take it instead as an opportunity to do some glorious refactoring to break that concrete dependency into an interface.
Q: How do I test an async method?
A: Make sure that your method returns Task, not void and it will Just Work (tm) like:
[When]
public async Task SomethingAsyncHappens()
{
_result = await MyAsyncThing();
}
Q: How do I contribute?
A: Fork and submit a pull request! For your pull request to be considered it should include tests as well as functional code.
Q: What license do you distribute the framework under?
A: LeapingGorilla.Testing is made available under the Apache 2.0 License. You are free to use the software in any way you choose as long as you adhere to the license.
Q: Do you provide any support for implementation?
A: We are open to talk with you or your business! Drop us a line via our contact form.
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. 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 was computed. 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. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
.NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
.NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
.NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
MonoAndroid | monoandroid was computed. |
MonoMac | monomac was computed. |
MonoTouch | monotouch was computed. |
Tizen | tizen40 was computed. tizen60 was computed. |
Xamarin.iOS | xamarinios was computed. |
Xamarin.Mac | xamarinmac was computed. |
Xamarin.TVOS | xamarintvos was computed. |
Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- microsoft.csharp (>= 4.7.0)
- nsubstitute (>= 5.3.0)
- System.Reflection.Emit (>= 4.7.0)
- System.Reflection.Emit.Lightweight (>= 4.7.0)
- XUnit (>= 2.9.3)
-
net8.0
- microsoft.csharp (>= 4.7.0)
- nsubstitute (>= 5.3.0)
- System.Reflection.Emit (>= 4.7.0)
- System.Reflection.Emit.Lightweight (>= 4.7.0)
- XUnit (>= 2.9.3)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.