XspecT 16.1.4
See the version list below for details.
dotnet add package XspecT --version 16.1.4
NuGet\Install-Package XspecT -Version 16.1.4
<PackageReference Include="XspecT" Version="16.1.4" />
paket add XspecT --version 16.1.4
#r "nuget: XspecT, 16.1.4"
// Install XspecT as a Cake Addin #addin nuget:?package=XspecT&version=16.1.4 // Install XspecT as a Cake Tool #tool nuget:?package=XspecT&version=16.1.4
XspecT: A fluent unit testing framework
Framework for writing and running automated tests in .Net in a fluent style, based on the popular "Given-When-Then" pattern, built upon XUnit, Moq, AutoMock, AutoFixture and FluentAssertions.
Whether you are beginner or expert in unit-testing, this framework will help you to write more descriptive, concise and maintainable tests.
Usage
It is assumed that you are already familiar with Xunit and Moq, or similar test- and mocking frameworks.
There is an accompanying independent assertion framework called XspecT.Assert
, which is built upon FluentAssertions,
but with a less worthy syntax, based on the verbs Is
, Has
and Does
instead of Should
.
Is-assertions are recommended over Should-assertions for making the tests read more like specifications, listing requirements rather than asserting expected results.
This is an example of a complete test class (specification) with one test method (requirement):
using XspecT.Verification;
using XspecT.Fixture;
using static App.Calculator;
namespace App.Test;
public class CalculatorSpec : Spec<int>
{
[Fact] public void WhenAdd_1_and_2_ThenSumIs_3() => When(_ => Add(1, 2)).Then().Result.Is(3);
}
When() is setting up the method to be called and Then() runs the test pipeline and provides the result.
Test a static method with [Theory]
If you are used to writing one test class per production class and use Theory for test input, you can use a similar style with XspecT.
First you create your test-class overriding Spec<[ReturnType]>
with the expected return type as generic argument.
Then create a test-method, attributed with Theory
and InlineData
, called When[Something]
.
This method call When
to setup the test pipeline with test data and the method to test.
Finally verify the result by calling Then().Result
(or only Result
) on the returned pipeline and check the result with Is
.
Example:
using XspecT.Verification;
using XspecT.Fixture;
using static App.Calculator;
namespace App.Test;
public class CalculatorSpec : Spec<int>
{
[Theory]
[InlineData(1, 1, 2)]
[InlineData(3, 4, 7)]
public void GivenTwoNumbers_WhenAdd_ReturnSum(int term1, int term2, int sum)
=> When(_ => Add(term1, term2)).Then().Result.Is(sum);
[Theory]
[InlineData(1, 1, 1)]
[InlineData(3, 4, 12)]
public void WhenMultiplyThenReturnProduct(int factor1, int factor2, int product)
=> When(_ => Multiply(factor1, factor2)).Then().Result.Is(product);
}
Recommended conventions
For more complex and realistic scenarios, it is recommended to create tests in a separate project from the production code, named [MyProject].Spec
.
The test-project should mimmic the production project's folder structure, but in addition have one folder for each class to test, named as the class.
Within that folder, create one test-class per method to test.
Test a static void method
- When testing a static void method, there is no return value to verify in result and by convention the generic TResult parameter should be set to object.
- However you can use
Throws
orNotThrows
to verify exceptions thrown.
Example:
namespace MyProject.Test.Validator;
public abstract class WhenVerifyAreEqual : Spec<object>
{
protected WhenVerifyAreEqual()
=> When(_ => MyProject.Validator.VerifyAreEqual(An<int>(), ASecond<int>()));
public class Given_1_And_2 : WhenVerifyAreEqual
{
[Fact] public void ThenThrows_NotEqual() => Given(1, 2).Then().Throws<NotEqual>();
}
public class Given_2_And_2 : WhenVerifyAreEqual
{
[Fact] public void ThenDoNotThrow() => Given(2, 2).Then().DoesNotThrow();
}
}
Test a class with dependencies
To test an instance method
[MyClass].[MyMethod]
, create an abstract class namedWhen[MyMethod]
inheritingXspecT.Spec<[MyClass], [TheResult]>
.The subject under test will be created automatically with mocks and default values by AutoMock.
Subject-under-test is available as the single input parameter to the lambda that is provided to the method
When
You can supply or modify you own constructor arguments by callingGiven
orGiven().Using
.To mock behaviour of any dependency call
Given<[TheService]>().That(_ => _.[TheMethod](...)).Returns/Throws(...)
.To verify a call to a dependency, write
Then<[TheService]>([SomeLambdaExpression])
.Both mocking and verification of behaviour is based on Moq framework.
Example:
namespace MyProject.Spec.ShoppingService;
public abstract class WhenPlaceOrder : Spec<MyProject.ShoppingService, object>
{
protected WhenPlaceOrder()
=> When(_ => _.PlaceOrder(An<int>()))
.Given<ICartRepository>().That(_ => _.GetCart(The<int>()))
.Returns(() => A<Cart>(_ => _.Id = The<int>()));
[Fact] public void ThenOrderIsCreated() => Then<IOrderService>(_ => _.CreateOrder(The<Cart>()));
[Fact] public void ThenLogsOrderCreated()
=> Then<ILogger>(_ => _.Information($"OrderCreated from Cart {The<int>()}"));
}
All the examples above also works for async methods.
More examples and features can be found as Unit tests in the source code.
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | 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. |
-
net8.0
- AutoFixture (>= 4.18.1)
- FluentAssertions (>= 6.12.0)
- Moq (>= 4.20.71)
- Moq.AutoMock (>= 3.5.0)
- xunit (>= 2.9.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 |
---|---|---|
17.1.5 | 124 | 11/10/2024 |
17.1.4 | 74 | 11/10/2024 |
17.1.3 | 73 | 11/3/2024 |
17.1.2 | 72 | 11/2/2024 |
17.1.1 | 147 | 10/29/2024 |
17.1.0 | 82 | 10/28/2024 |
17.0.3 | 117 | 10/14/2024 |
17.0.2 | 78 | 10/14/2024 |
17.0.1 | 78 | 10/14/2024 |
17.0.0 | 87 | 10/12/2024 |
17.0.0-pre.3 | 45 | 10/8/2024 |
17.0.0-pre.2 | 41 | 10/6/2024 |
17.0.0-pre.1 | 43 | 10/6/2024 |
16.4.1 | 93 | 10/5/2024 |
16.4.0 | 113 | 10/4/2024 |
16.3.1 | 84 | 9/22/2024 |
16.3.0 | 89 | 9/22/2024 |
16.2.1 | 215 | 9/14/2024 |
16.2.0 | 94 | 9/14/2024 |
16.1.5 | 89 | 9/7/2024 |
16.1.4 | 96 | 9/7/2024 |
16.1.3 | 162 | 9/7/2024 |
16.1.2 | 87 | 9/6/2024 |
16.1.1 | 100 | 9/3/2024 |
16.1.0 | 97 | 9/2/2024 |
16.0.4 | 278 | 8/18/2024 |
16.0.4-preview | 110 | 8/18/2024 |
16.0.3-preview | 105 | 8/18/2024 |
16.0.2-preview | 109 | 8/17/2024 |
16.0.1-preview | 110 | 8/17/2024 |
16.0.0-preview | 105 | 8/16/2024 |
15.7.0 | 176 | 8/7/2024 |
15.6.2 | 78 | 7/29/2024 |
15.6.1 | 179 | 7/14/2024 |
15.6.0 | 89 | 7/13/2024 |
15.5.4 | 149 | 7/7/2024 |
15.5.3 | 103 | 7/7/2024 |
15.5.2 | 102 | 7/7/2024 |
15.5.1 | 100 | 7/2/2024 |
15.5.0 | 105 | 6/30/2024 |
15.4.1 | 101 | 6/29/2024 |
15.4.0 | 119 | 6/24/2024 |
15.3.2 | 95 | 6/24/2024 |
15.3.1 | 113 | 6/23/2024 |
15.3.0 | 115 | 6/23/2024 |
15.2.1 | 121 | 6/20/2024 |
15.2.0 | 118 | 6/19/2024 |
15.1.3-preview | 98 | 6/19/2024 |
15.1.2 | 111 | 6/18/2024 |
15.1.1 | 127 | 6/17/2024 |
15.1.0 | 126 | 6/16/2024 |
15.0.1 | 109 | 6/15/2024 |
15.0.0 | 108 | 6/9/2024 |
14.2.1 | 103 | 6/6/2024 |
14.2.0 | 100 | 6/6/2024 |
14.1.0 | 113 | 5/13/2024 |
14.0.0 | 102 | 5/9/2024 |
13.3.2 | 228 | 4/7/2024 |
13.3.1 | 118 | 1/31/2024 |
13.3.0 | 333 | 1/20/2024 |
13.2.3 | 123 | 1/15/2024 |
13.2.2 | 118 | 1/13/2024 |
13.2.1 | 125 | 1/2/2024 |
13.2.0 | 171 | 1/2/2024 |
13.1.2 | 131 | 12/19/2023 |
13.1.1 | 165 | 12/19/2023 |
13.1.0 | 136 | 12/18/2023 |
13.0.1 | 109 | 12/17/2023 |
13.0.0 | 120 | 12/17/2023 |
12.2.2 | 121 | 12/16/2023 |
12.2.1 | 115 | 12/16/2023 |
12.2.0 | 119 | 12/16/2023 |
12.1.1 | 125 | 12/16/2023 |
12.1.0 | 149 | 12/3/2023 |
12.0.0 | 137 | 12/2/2023 |
11.0.4 | 141 | 11/28/2023 |
11.0.3 | 237 | 11/19/2023 |
11.0.2 | 132 | 11/19/2023 |
11.0.1 | 137 | 11/18/2023 |
11.0.0 | 134 | 11/18/2023 |
10.0.2 | 144 | 11/18/2023 |
10.0.1 | 130 | 11/15/2023 |
10.0.0 | 138 | 11/12/2023 |
9.3.2 | 124 | 11/12/2023 |
9.3.1 | 124 | 11/12/2023 |
9.3.0 | 133 | 11/11/2023 |
9.2.1 | 136 | 11/11/2023 |
9.2.0 | 137 | 11/5/2023 |
9.1.1 | 143 | 10/29/2023 |
9.1.0 | 146 | 10/28/2023 |
9.0.0 | 155 | 10/28/2023 |
8.5.1 | 149 | 10/27/2023 |
8.5.0 | 149 | 10/26/2023 |
8.4.0 | 163 | 10/22/2023 |
8.3.1 | 160 | 10/22/2023 |
8.3.0 | 153 | 10/22/2023 |
8.2.1 | 149 | 10/22/2023 |
8.2.0 | 139 | 10/21/2023 |
8.1.2 | 146 | 10/21/2023 |
8.1.1 | 142 | 10/20/2023 |
8.1.0 | 131 | 10/20/2023 |
8.0.1 | 154 | 10/18/2023 |
8.0.0 | 136 | 10/16/2023 |
7.2.0 | 144 | 10/16/2023 |
7.1.1 | 139 | 10/12/2023 |
7.1.0 | 164 | 10/8/2023 |
7.0.1 | 136 | 10/1/2023 |
7.0.0 | 133 | 10/1/2023 |
6.4.0 | 154 | 9/30/2023 |
6.3.2 | 123 | 9/30/2023 |
6.3.1 | 147 | 9/30/2023 |
6.3.0 | 124 | 9/25/2023 |
6.2.4 | 146 | 9/15/2023 |
6.2.3 | 140 | 9/15/2023 |
6.2.2 | 128 | 9/15/2023 |
6.2.1 | 156 | 9/15/2023 |
6.2.0 | 143 | 9/14/2023 |
6.1.3 | 154 | 9/13/2023 |
6.1.2 | 167 | 9/12/2023 |
6.1.1 | 148 | 9/12/2023 |
6.1.0 | 175 | 9/10/2023 |
6.0.0 | 148 | 9/9/2023 |
5.5.0 | 159 | 9/8/2023 |
5.4.3 | 146 | 9/7/2023 |
5.4.2 | 167 | 9/5/2023 |
5.4.1 | 137 | 9/3/2023 |
5.4.0 | 236 | 8/28/2023 |
5.3.1 | 171 | 8/28/2023 |
5.3.0 | 149 | 8/27/2023 |
5.2.0 | 167 | 8/27/2023 |
5.1.1 | 165 | 8/26/2023 |
5.1.0 | 161 | 8/26/2023 |
5.0.0 | 170 | 8/26/2023 |
4.5.2 | 148 | 8/26/2023 |
4.5.1 | 153 | 8/26/2023 |
4.5.0 | 145 | 8/26/2023 |
4.4.7 | 157 | 8/22/2023 |
4.4.6 | 141 | 8/22/2023 |
4.4.5 | 137 | 8/21/2023 |
4.4.4 | 169 | 8/20/2023 |
4.4.3 | 162 | 8/16/2023 |
4.4.2 | 173 | 8/15/2023 |
4.4.1 | 170 | 8/15/2023 |
4.4.0 | 188 | 8/15/2023 |
4.3.1 | 168 | 8/14/2023 |
4.3.0 | 188 | 8/14/2023 |
4.2.0 | 178 | 8/14/2023 |
4.1.1 | 169 | 8/11/2023 |
4.1.0 | 166 | 8/9/2023 |
4.0.0 | 182 | 8/8/2023 |
3.3.2 | 165 | 8/7/2023 |
3.3.1 | 162 | 8/6/2023 |
3.3.0 | 168 | 8/6/2023 |
3.2.1 | 163 | 8/6/2023 |
3.2.0 | 201 | 8/6/2023 |
3.1.0 | 195 | 8/5/2023 |
3.0.0 | 184 | 8/2/2023 |
2.4.1 | 180 | 8/1/2023 |
2.4.0 | 168 | 8/1/2023 |
2.3.1 | 175 | 7/30/2023 |
2.3.0 | 166 | 7/30/2023 |
2.2.3 | 167 | 7/29/2023 |
2.2.2 | 177 | 7/28/2023 |
2.2.1 | 173 | 7/24/2023 |
2.2.0 | 182 | 7/24/2023 |
2.1.1 | 182 | 7/23/2023 |
2.1.0 | 179 | 7/23/2023 |
2.0.1 | 181 | 7/21/2023 |
2.0.0 | 189 | 7/21/2023 |
1.1.0 | 197 | 7/20/2023 |
1.0.0 | 163 | 7/20/2023 |
Concrete class instance provided as Given or Given().Using will be applied also when any of it's interfaces are used - no need for explicit cast to interface