ToolBX.Eloquentest 2.2.0-beta2

This is a prerelease version of ToolBX.Eloquentest.
There is a newer version of this package available.
See the version list below for details.
dotnet add package ToolBX.Eloquentest --version 2.2.0-beta2                
NuGet\Install-Package ToolBX.Eloquentest -Version 2.2.0-beta2                
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="ToolBX.Eloquentest" Version="2.2.0-beta2" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add ToolBX.Eloquentest --version 2.2.0-beta2                
#r "nuget: ToolBX.Eloquentest, 2.2.0-beta2"                
#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 ToolBX.Eloquentest as a Cake Addin
#addin nuget:?package=ToolBX.Eloquentest&version=2.2.0-beta2&prerelease

// Install ToolBX.Eloquentest as a Cake Tool
#tool nuget:?package=ToolBX.Eloquentest&version=2.2.0-beta2&prerelease                

Eloquentest

Eloquentest

A simple to use .NET unit testing framework built on top of MSTest and Moq. It also includes built-in support for services that are injected using [AutoInject].

⚠️ Eloquentest is moving away from base Tester classes in version 3.0.0 and will instead rely on extension and utility methods to give you the tools to build your own base Tester classes that can implement whichever framework that you like. The current implementation using Moq and AutoFixture will likely be part of an Eloquentest.Classic package and may become deprecated in version 4.0.0.

Eloquentest will also be separated into multiple packages supporting different frameworks such as Moq , NSubstitute, AutoFixture, etc.

Getting started

Here is the dumbest service you can imagine.

    public class DumbestServiceYouCanImagine
    {
        public string Format(int number)
        {
            return number == 0 ? "It's zero" : "It's not zero";
        }
    }

Here is the dumbest unit test for the dumbest service you can imagine.

    [TestClass]
    public class DumbestServiceYouCanImagineTester : Tester<DumbestServiceYouCanImagine>
    {
        [TestMethod]
        public void WhenNumberIsZero_ReturnItsZero()
        {
            //Act
            var result = Instance.Format(0);

            //Assert
            Assert.AreEqual("It's zero", result);
        }
    }

That's all well and good but what if your dumb service had dependencies to other services though?

public interface ISomeOtherService
{
    public string UserId { get; }
    public IReadOnlyList<int> Roles { get; }
}
public class DumbestServiceYouCanImagine
{
    private readonly ISomeOtherService _someOtherService;

    public DumbestServiceYouCanImagine(ISomeOtherService someOtherService)
    {
        _someOtherService = someOtherService;
    }

    public string DoSomeOtherStuff()
    {
        return _someOtherService.Roles.Contains(8) ? 
            $"User {_someOtherService.UserId} is authorized to do dumb stuff." : 
            $"User {_someOtherService.UserId} is strictly forbidden from doing dumb stuff!";
    }
}
[TestClass]
public class DumbestServiceYouCanImagineTester : Tester<DumbestServiceYouCanImagine>
{
    [TestMethod]
    public void WhenContainsRoleNumberEight_SayThatUserIsAuthorized()
    {
        //Arrange
        var userId = Fixture.Create<string>();
        GetMock<ISomeOtherService>().Setup(x => x.UserId).Returns(userId);
        
        GetMock<ISomeOtherService>().Setup(x => x.Roles).Returns(new List<int> { 8 });

        //Act
        var result = Instance.DoSomeOtherStuff();

        //Assert
        Assert.AreEqual($"User {userId} is authorized to do dumb stuff.", result);
    }

    [TestMethod]
    public void WhenDoesNotContainRoleNumberEight_SayThatUserIsUnauthorized()
    {
        //Arrange
        var userId = Fixture.Create<string>();
        GetMock<ISomeOtherService>().Setup(x => x.UserId).Returns(userId);

        GetMock<ISomeOtherService>().Setup(x => x.Roles).Returns(new List<int>());

        //Act
        var result = Instance.DoSomeOtherStuff();

        //Assert
        Assert.AreEqual($"User {userId} is strictly forbidden from doing dumb stuff!", result);
    }
}

[AutoCustomization]

//This Customization will be applied project-wide so you don't have to remember to add them yourself each time in TestInitialize
[AutoCustomization]
public class SomeCustomization : ICustomization
{
    ...
}

//Also works with ISpecimenBuilder
[AutoCustomization]
public class SomeSpecimenBuilder : ISpecimenBuilder
{
    ...
}

Testing collections

Ever wanted to test whichever item from a collection?

[TestMethod]
public void WhenYouWantWhichever_GetWhichever()
{
    //Arrange
    var list = Fixture.CreateMany<string>().ToList();

    var something = Fixture.Create<Something>();
    GetMock<ISomeShadyService>().Setup(x => x.GetSomething(list.GetRandom())).Returns(something);

    //Act
    var result = Instance.DoSomething(list);

    //Assert
    result.Should.BeEquivalentTo(something);
}

You can also do the same thing but with the index only if that's what floats your boat.

var index = list.GetRandomIndex();

Using the generic tester with custom constructor parameters

It�s all well and good but what if you want to use the generic Tester class while providing your own parameters to the tested class� constructor instead of the automatically generated mocks and fixtures?

You can do that with the ConstructWith() method.

[AutoInject]
public class GameObjectFactory : IGameObjectFactory
{
    private readonly IServiceProvider _serviceProvider;

    public GameObjectFactory(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }

    public IGameObject Create(GameObjectConfiguration configuration) => new GameObject(_serviceProvider, configuration);
}

public record GameObjectConfiguration(string Name, Vector2<float> InitialPosition, Direction InitialDirection);

//The service provider is passed by the GameObjectFactory and is always the same reference
//Configuration is unique to every instance so we need to be able to override it if we want to test that things are correctly initialized
public GameObject(IServiceProvider serviceProvider, GameObjectConfiguration configuration)
{
    _game = serviceProvider.GetRequiredService<IGame>();
    Name = configuration.Name;
    Position = configuration.InitialPosition;
    Direction = configuration.InitialDirection;
}

[TestClass]
public class GameObjectTester 
{
    [TestClass]
    public class Move : Tester<GameObject>
    {
        [TestMethod]
        public void Always_ChangePosition()
        {
            //Arrange
            var configuration = Fixture.Create<GameObjectConfiguration>();

            //It is important to call ConstructWith before accessing the Instance property!
            ConstructWith(configuration);

            //Act
            Instance.Move(new Vector2<float>(1, 0));

            //Assert
            Instance.Position.Should().Be(configuration.InitialPosition + new Vector2<float>(1, 0));
        }
    }
}

AutoFillTester

Works just like Tester<T> except that it automatically fills your Instance's public set and init with values. Works in a lot of cases but you might want to stick with Tester<T> for others.

Integration tests

The Eloquentest.Integration namespace (available on nuget.org as a separate package) provides tools to leverage MSTest to execute code without mocking all while using the Eloquentest structure and syntax you may already be familiar with.

IntegrationTester and IntegreationTester<T> replace Tester and Tester<T> and there are no mocks.

Empty string cases

You can test string.IsNullOrEmpty and string.IsNullOrWhitespace cases using the following methods.

[TestMethod]
public void WhenDirectoryIsNullOrEmpty_Throw() => WhenIsNullOrEmpty(directory =>
{
    //Arrange

    //Act
    var action = () => Instance.DeleteDirectory(directory);

    //Assert
    action.Should().Throw<ArgumentNullException>().WithParameterName(nameof(directory));
});
[TestMethod]
public void WhenFilenameIsNullOrWhitespace_Throw() => WhenIsNullOrWhiteSpace(filename =>
{
    //Arrange
            
    //Act
    var action = () => Instance.DeleteFile(filename);

    //Assert
    action.Should().Throw<ArgumentNullException>().WithParameterName(nameof(filename));
});

These helper methods will execute your tests automatically using all cases of empty, null and white spaces without having to rely on [DataRow]. The method above is the equivalent of this :

[TestMethod]
[DataRow("")]
[DataRow(" ")]
[DataRow(null)]
[DataRow("\n")]
[DataRow("\r")]
[DataRow("\t")]
public void WhenFilenameIsNullOrWhitespace_Throw(string filename)
{
    //Arrange
            
    //Act
    var action = () => Instance.DeleteFile(filename);

    //Assert
    action.Should().Throw<ArgumentNullException>().WithParameterName(nameof(filename));
});

Setup

It works right out of the box if you already use AutoInject in your regular code.

Breaking changes

1.0.X → 1.1.X GetRandom and GetRandomIndex methods have been removed from Eloquentest. Please import and use ToolBX.OPEX from nuget.org instead.

2.0.X → 2.1.0 AutoFillTester<T> was addded. There have been minor changes in when things are instantiated which may affect some users in Tester and Tester<T>.

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

NuGet packages (2)

Showing the top 2 NuGet packages that depend on ToolBX.Eloquentest:

Package Downloads
ToolBX.Eloquentest.Dummies

Package Description

ToolBX.Eloquentest.AutoFixture

Package Description

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
3.0.0 3,337 9/27/2024
3.0.0-beta4 300 9/22/2024
3.0.0-beta3 77 9/22/2024
3.0.0-beta2 127 9/7/2024
3.0.0-beta1 182 8/27/2024
2.2.1 3,930 1/17/2024
2.2.1-beta.1 67 1/14/2024
2.2.0 249 1/11/2024
2.2.0-beta97 167 1/7/2024
2.2.0-beta96 103 1/4/2024
2.2.0-beta94 122 12/29/2023
2.2.0-beta93 136 12/29/2023
2.2.0-beta92 102 12/29/2023
2.2.0-beta91 120 12/29/2023
2.2.0-beta9 148 12/14/2023
2.2.0-beta8 107 12/13/2023
2.2.0-beta7 140 11/24/2023
2.2.0-beta6 106 11/22/2023
2.2.0-beta5 108 11/22/2023
2.2.0-beta4 117 11/17/2023
2.2.0-beta3 100 11/17/2023
2.2.0-beta2 94 11/17/2023
2.2.0-beta11 133 12/27/2023
2.2.0-beta10 124 12/14/2023
2.2.0-beta1 108 11/17/2023
2.2.0-beta.12 85 12/28/2023
2.2.0-alpha.1 80 12/28/2023
2.1.4 2,761 11/22/2023
2.1.4-beta4 106 11/22/2023
2.1.4-beta3 115 11/17/2023
2.1.4-beta2 122 11/2/2023
2.1.4-beta1 105 10/28/2023
2.1.3 2,582 10/17/2023
2.1.3-beta1 103 10/17/2023
2.1.2 559 10/4/2023
2.1.2-beta7 145 10/3/2023
2.1.2-beta6 717 8/29/2023
2.1.2-beta5 109 8/28/2023
2.1.2-beta4 109 8/25/2023
2.1.2-beta3 119 8/24/2023
2.1.2-beta2 1,865 6/23/2023
2.1.2-beta1 134 6/22/2023
2.1.1 331 6/19/2023
2.1.0 258 6/13/2023
2.1.0-beta3 157 6/13/2023
2.1.0-beta2 134 6/12/2023
2.1.0-beta1 145 6/10/2023
2.0.2 978 4/25/2023
2.0.1 206 4/23/2023
2.0.0 410 11/9/2022
2.0.0-beta2 165 9/28/2022
2.0.0-beta1 161 9/20/2022
1.1.2 425 9/28/2022
1.1.1 432 9/20/2022
1.1.1-beta3 173 8/10/2022
1.1.1-beta2 149 8/10/2022
1.1.1-beta1 147 8/10/2022
1.1.0 407 8/10/2022
1.0.7 407 5/27/2022
1.0.6 419 5/16/2022
1.0.5 457 3/15/2022
1.0.5-beta 167 3/15/2022
1.0.4 470 2/5/2022
1.0.3 436 2/4/2022
1.0.2 429 1/14/2022
1.0.1 265 12/22/2021
1.0.0 302 12/15/2021