XspecT 4.1.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package XspecT --version 4.1.0                
NuGet\Install-Package XspecT -Version 4.1.0                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="XspecT" Version="4.1.0" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add XspecT --version 4.1.0                
#r "nuget: XspecT, 4.1.0"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install XspecT as a Cake Addin
#addin nuget:?package=XspecT&version=4.1.0

// Install XspecT as a Cake Tool
#tool nuget:?package=XspecT&version=4.1.0                

XspecT: A fluent unit testing framework

Framework for writing and running automated tests in .Net in a flexible and 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. This package includes FluentAssertions, but also comes with its own, more limited but less wordy assertion methods, based on the verb Is instead of Should. Is-assertions have the same return-types as Should-assertions, so they can be combined in the same sentence.

Is-assertions are recommended to make 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 : StaticSpec<int>
{
    [Fact] public void WhenAdd_1_and_2_ThenSumIs_3() => Given(1, 2).When(Add).Then.Result.Is(3);
}

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 StaticSpec<[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 Given and When, in any order, 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:

public class CalculatorSpec : StaticSpec<int>
{
    [Theory]
    [InlineData(1, 1, 2)]
    [InlineData(3, 4, 7)]
    public void GivenTwoNumbers_WhenAdd_ReturnSum(int x, int y, int sum)
        => Given(x, y).When(Calculator.Add).Result.Is(sum);

    [Theory]
    [InlineData(1, 1, 1)]
    [InlineData(3, 4, 12)]
    public void WhenMultiplyThenReturnProduct(int x, int y, int product)
        => Given(x, y).When(Calculator.Multiply).Result.Is(product);
}

For more complex and realistic scenarios, it is recommended to create tests in a separate project from the production code, named [MyProject].Test. 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 is set to object.
  • However you can use Throws or NotThrows to verify exceptions thrown.

Example:

namespace MyProject.Test.Validator;

public abstract class WhenVerifyAreEqual : StaticSpec<object>
{
    protected WhenVerifyAreEqual() => When<int, int>(MyProject.Validator.VerifyAreEqual);

    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.NotThrows();
    }
}

Test a class with dependencies

  • To test an instance method [MyClass].[MyMethod], inherit XspecT.SubjectSpec<[MyClass], TResult>.

  • It is recommended practice to create a common baseclass for all tests of [MyClass], named [MyClass]Spec.

  • The subject under test (sut) will be created automatically with mocks and default values by AutoMock. You can supply you own constructor arguments by calling Using (which will be applied in the same order when test pipeline is executed).

  • For each method to test, create an abstract class named When[MyMethod] inheriting [MyClass]Spec in the same way as for static methods.

  • To mock behaviour of any dependency, either override Setup or provide the mocking by calling GivenThat. Each call to GivenThat will provide additional arrangement that will be applied on test execution on the inversed order.

  • The framework gives you direct access to one (lazily generated) mock each of any class type type. You can access a mock by The<MyMockedInterface>().

  • To verify a call to a dependency, write Then.Does<MyMockedInterface>([SomeLambdaExpression]).

  • Moq framework is used to express both mocking and verification of behaviour.

Example:

namespace MyProject.Test.ShoppingService;

public abstract class ShoppingServiceSpec<TResult> 
    : SubjectSpec<MyProject.ShoppingService, TResult>
{
    protected const int ShopId = 2;

    //Specify a value to pass to the constructor
    protected ShoppingServiceSpec() => Using(ShopId);
}

public abstract class WhenPlaceOrder : ShoppingServiceSpec<object>
{
    protected ShoppingCart Cart;

    protected WhenPlaceOrder() 
        => When(() => SUT.PlaceOrder(Cart.Id)).
        GivenThat(() => The<ICartRepository>().Setup(_ => _.GetCart(Cart.Id)).Returns(Cart));

    public class GivenCart : WhenPlaceOrder
    {
        public GivenCart() => GivenThat(() => Cart = new() { Id = 123 });

        [Fact] public void ThenOrderIsCreated() 
            => Then.Does<IOrderService>(_ => _.CreateOrder(Cart));

        [Fact] public void ThenLogsOrderCreated()
            => Then.Does<ILogger>(
                _ => _.Information($"OrderCreated from Cart {Cart.Id} in Shop {ShopId}"));
    }
}

Test async methods

All the examples above also works for async methods, with small modifications.

More examples can be found as Unit tests in the source code.

Product 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 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 was computed.  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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

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

Only use Given in static pipelines (GivenThat can be used for both static and subject)