Oatmilk.Xunit 0.1.3

dotnet add package Oatmilk.Xunit --version 0.1.3                
NuGet\Install-Package Oatmilk.Xunit -Version 0.1.3                
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="Oatmilk.Xunit" Version="0.1.3" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Oatmilk.Xunit --version 0.1.3                
#r "nuget: Oatmilk.Xunit, 0.1.3"                
#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 Oatmilk.Xunit as a Cake Addin
#addin nuget:?package=Oatmilk.Xunit&version=0.1.3

// Install Oatmilk.Xunit as a Cake Tool
#tool nuget:?package=Oatmilk.Xunit&version=0.1.3                

<p align="center"><a href="https://www.nuget.org/packages/Oatmilk/"><img src="https://img.shields.io/nuget/v/Oatmilk?style=flat&label=Oatmilk" alt="NuGet Version"></a> <a href="https://www.nuget.org/packages/Oatmilk.Nunit/"><img alt="NuGet Version" src="https://img.shields.io/nuget/v/Oatmilk.Nunit?style=flat&label=Oatmilk.Nunit"></a> <a href="https://www.nuget.org/packages/Oatmilk.Xunit/"><img alt="NuGet Version" src="https://img.shields.io/nuget/v/Oatmilk.Xunit?style=flat&label=Oatmilk.Xunit"></a> <a href="https://www.nuget.org/packages/Oatmilk.MSTest/"><img alt="NuGet Version" src="https://img.shields.io/nuget/v/Oatmilk.MSTest?style=flat&label=Oatmilk.MSTest"></a> <a href="https://opensource.org/licenses/MIT" rel="nofollow"><img alt="License: MIT" src="https://img.shields.io/badge/License-MIT-yellow.svg"></a> <a href="https://discord.gg/QbmMq6snYg"><img src="https://img.shields.io/discord/1267395588513726605?logo=discord" alt="Discord"/> </p> <p align="center"><a href="https://github.com/LiamMorrow/Oatmilk/actions"><img src="https://img.shields.io/github/actions/workflow/status/LiamMorrow/Oatmilk/build.yml" alt="GitHub Actions Workflow Status"></a> <a href="https://codecov.io/github/LiamMorrow/Oatmilk"><img alt="codecov" src="https://codecov.io/github/LiamMorrow/Oatmilk/graph/badge.svg?token=5UVDXIJVGV"></a></p>

<p> </p>

<p align="center"><img alt="Oatmilk Mascot - Oatie" src="./Assets/Oatie.png" width=256/></p>

<h2 align="center">🥛 Refreshing .NET Testing</h2>

"Jest is great, let's bring it to .NET" - Me

Oatmilk is a testing library for .NET which allows you to write declarative tests, free from annotations and long method names. It is heavily inspired by the jest and mocha testing frameworks in the JavaScript ecosystem.

Oatmilk currently supports running in a test project configured with xunit or nunit, with limited support for MSTest. You can run your existing xunit/nunit Facts and Tests alongside Oatmilk tests, so you don't need to convert your entire test suite to Oatmilk all at once.

Note that Oatmilk does not intend to be a full test framework, as such, things like mocking and asserting are out of scope. The assertions provided by Xunit/Nunit/MsTest are entirely compatible with Oatmilk. There are also many other great tools for the job. Have a look at FluentAssertions for a great assertions library. Or NSubstitute for your mocking needs.

Getting Started

First in your test project, install the appropriate Oatmilk package.

Dotnet CLI
dotnet add package Oatmilk.Nunit
# OR dotnet add package Oatmilk.Xunit
# OR dotnet add package Oatmilk.MSTest

Then create a test class, and create your first Oatmilk Test by using the Describe attribute on a method. Be sure to include a static import of Oatmilk.TestBuilder. If using MSTest, your test class will still require a [TestClass] attribute.

using Oatmilk;
using static Oatmilk.TestBuilder;

// [TestClass] if using MSTest
public class MyTestClass
{
    [Describe("My Test Suite")]
    public void Spec()
    {
        // Describe as many tests as you'd like
        It("Should pass", () => Assert.True(true));

        It("Is another test, wow!", () => Assert.Equal(1, 1));
    }

    [Fact]
    public void ExistingXunitTest()
    {
        // You don't need to throw away your existing tests - they can live alongside Oatmilk tests :)
    }
}

Your tests should now show up in your IDE, and can be run with:

dotnet test

You can run specific tests by filtering:

dotnet test --filter "Name~My Test Suite"

Test setup and teardown

It is often useful to have setup and teardown methods that run around tests. Oatmilk exposes these mechanisms through four methods:

  • BeforeAll - Runs ONCE before any test has run in its scope and child scopes
  • BeforeEach - Runs before EVERY test in its scope and child scopes
  • AfterEach - Runs after EVERY test in its scope and child scopes
  • AfterAll - Runs ONCE after all tests have run in its scope and child scopes

Each of these mechanisms support async operations.

Scopes are defined by Describe blocks and can be nested.

Example:

public class MyTestClass
{
    [Describe("My Test Suite")]
    public void Spec()
    {
        Guid aGuidThatIsUniqueForEachTest;

        BeforeAll(()=>
        {
            // This will run once if any of the tests are run
        })

        BeforeEach(()=>
        {
            // This will run every single time a test is run
            // You can put initialization code here
            aGuidThatIsUniqueForEachTest = Guid.NewGuid();
        })

        It("Is a test in the top scope", () =>
        {
            // At this stage, the BeforeAll, and BeforeEach defined above will have run for this test
        })

        AfterEach(ctx =>{
            // This will run after every single test in this scope and child scopes
            // ctx contains information about the test which just ran (did it pass?)
        })

        Describe("A nested scope", () =>
        {
            BeforeEach(()=>{
                // This will only run for test which are declared in this scope, or any scopes declared WITHIN this scope
            })

            It("Is a nested test", () => {
                // At this stage:
                // The first BeforeAll will have only run ONCE - even if we are running both tests
                // The BeforeEach in the parent scope ran for this test
                // The BeforeEach in this scope ran for this test
            })
        })

    }
}

Features

Type Safe Test Enumeration

Providing tests with dynamic data has next to zero ceremony. There's no need to annotate with a method with [Theory], or supply data through a different class / member with [MemberData], which has many pitfalls and moves the information of the test far from it. Simply use an It.Each or a Describe.Each method call and provide the data directly.


It.Each(
    [1, 3, 5],
    val => $"Asserts that {val} is odd" // Format string are supported as well: "{0} is odd",
    (value)=>
    {
        (value % 2).Should().Be(1);
    });

Easily Skip Tests

Got a test you'd like to skip for the time being? Simply add a .Skip to the test or describe block and it will be skipped.


It.Skip("should be skipped", () => Assert.True(false));

Describe.Skip("every test in this scope will be skipped", () =>
{
    It("will be skipped", () => Assert.True(false));
})

Isolate Tests With Only

Focusing on a single test in a spec? Simply use It.Only or Describe.Only and Oatmilk will skip every other test in that spec.


It.Only("will run", () => Assert.True(true));

It("will not run since another test uses It.Only", () => Assert.True(false));

Describe.Only("A situation where all other tests will be skipped", () =>
{
    It("will run", () => Assert.True(true));
})

It("will not run because the describe block above uses .Only", () => Assert.True(false));

Fluent Syntax

Many people use the csharpier formatter to format their code. Unfortunately, the way that it chops arguments produces somewhat less than nice to read Oatmilk test code, as it will put the description of the test on a new line:

It(
    "is a test",
    () =>
    {
        // My test code
    }
)

For this reason, Oatmilk exposes a fluent API where the body of the test is supplied in a fluent manner, rather than as a second argument:

It("is a test")
    .When(() =>
    {
        // My test code
    });

This is purely stylistic. The two methods are functionally identical. If you don't like the name "When", simply create an extension method on the ItBlock and DescribeBlock classes with your chosen name. Describe has a similar approach, however it uses the method As, rather than When:

Describe("My test suite")
    .As(() =>
    {
        // My describe block
    })

IDE Feature Matrix

Each test runner exposes different level of support for Oatmilk. All of them support running the entire suite of tests.

Feature Nunit Xunit MSTest
Running all tests via dotnet test
Running individual test via dotnet test --filter
Debugging entire test suite via IDE
Debugging single test via IDE
Run individual tests via IDE
Jump to It block via IDE
Run/Debug specific Describe block via IDE
View tests as hierarchy in IDE
Jump to nested Describe block via IDE

Examples

See the Oatmilk.Tests packages for examples of how tests can be written. All tests for Oatmilk are written in it!

Product 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. 
.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. 
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
0.1.3 80 8/3/2024
0.1.2 71 8/3/2024
0.1.1 53 7/31/2024
0.1.0 58 7/31/2024
0.0.11 56 7/30/2024
0.0.10 52 7/29/2024
0.0.9 67 7/29/2024
0.0.8 124 7/28/2024

Add support for older .NET versions