SutForge 1.0.0
dotnet add package SutForge --version 1.0.0
NuGet\Install-Package SutForge -Version 1.0.0
<PackageReference Include="SutForge" Version="1.0.0" />
paket add SutForge --version 1.0.0
#r "nuget: SutForge, 1.0.0"
// Install SutForge as a Cake Addin #addin nuget:?package=SutForge&version=1.0.0 // Install SutForge as a Cake Tool #tool nuget:?package=SutForge&version=1.0.0
SUT Forge
<p align="center"> <img src="favicon.png" alt="Icon" /> </p>
Simplifying .NET unit testing and Test-Driven Development with an intuitive builder pattern, classical approach, and high extensibility for effortless creation and customization of System Under Test instances.
Introduction
Experience unit testing and Test-Driven Development (TDD) in a whole new light with SUTForge
, a simple yet powerful .NET package meticulously designed to streamline the creation of System Under Test (SUT) instances.
SUTForge
follows the Chicago School to unit testing (also referred to as the Classical School of unit testing), offering a clean and efficient alternative to the over-mocking issues associated with the London approach.
What sets SUTForge
apart is its high degree of extensibility, allowing developers to craft personalized extension methods and tailor SUTs to their specific testing needs.
Key Features:
Effortless TDD Integration:
SUTForge
seamlessly integrates with Test-Driven Development (TDD), enabling developers to write tests first and then effortlessly construct and configure their SUTs using the intuitive builder pattern provided bySUTForge
.Chicago School of Unit Testing: Embrace the Chicago approach of unit testing, emphasizing simplicity and readability.
SUTForge
encourages clean and maintainable tests without succumbing to over-mocking pitfalls, ensuring your unit tests remain valuable assets throughout the development lifecycle.Intuitive Builder Pattern: Constructing SUTs becomes a breeze with
SUTForge
's intuitive builder pattern. Say goodbye to the complexities of manual setup and embrace a syntax that makes unit test code concise, expressive, and easy to understand.Highly Extensible:
SUTForge
goes beyond the basics, offering developers a high degree of extensibility. Write your own extension methods to customize SUTs according to your unique testing requirements. TailorSUTForge
to fit seamlessly into your specific development workflow.
đź’Ş Elevate your unit testing and TDD experience with SUTForge
—a library that not only embraces the Chicago approach but also empowers you to extend and customize your testing capabilities. Your journey to efficient and maintainable tests starts here!
Getting Started
To get started with SUTForge
, you need to install the package in your .NET project. Once installed, you can start writing your unit tests using the SUTForge
's intuitive builder pattern.
Installation
Install SUTForge
via .NET CLI:
- 📦 NuGet:
dotnet add package SUTForge
Writing Your First Test
Here's an example of how you can write a unit test using SUTForge
. This test checks whether a service is correctly built by the builder.
[Test]
public async Task Services_are_built_by_the_builder()
{
// Arrange
// Act
var sut = await SutBuilder.Create
.ConfigureServices(services => { services.AddSingleton<ISomeInterface, SomeImplementation>(); })
.BuildAsync();
// Assert
sut.Should().NotBeNull();
sut.GetService<ISomeInterface>().Should().NotBeNull().And.BeOfType<SomeImplementation>();
}
In the above test, we're using the SutBuilder.Create
method to create a new instance of the SUT builder. We then configure the services by adding a singleton service of type ISomeInterface
with an implementation of SomeImplementation
. After building the SUT, we assert that the SUT is not null and that the service of type ISomeInterface
is not null and is of type SomeImplementation
.
Resolving Configuration for Services
SUTForge
also allows you to resolve configurations for services. Here's an example:
Considering the following class that make use of IConfiguration
:
class ClassWithConfiguration
{
private readonly IConfiguration _configuration;
public ClassWithConfiguration(IConfiguration configuration)
{
_configuration = configuration;
}
// ...
}
In this test, we're adding a service of type ClassWithConfiguration
that requires configuration:
[Test]
public async Task Configuration_is_resolved_for_services()
{
// Arrange
// Act
var sut = await SutBuilder.Create
.ConfigureServices(services => { services.AddSingleton<ClassWithConfiguration>(); })
.BuildAsync();
// Assert
sut.GetService<ClassWithConfiguration>().Should().NotBeNull();
}
After building the SUT, we assert that the service of type ClassWithConfiguration
is not null, implying that the configuration was resolved correctly.
Customizing Configuration
SUTForge
also allows you to customize the configuration. Here's an example:
[Test]
public async Task Configuration_can_be_customized()
{
// Arrange
// Act
var sut = await SutBuilder.Create
.ConfigureServices(services => { services.AddSingleton<ClassWithConfiguration>(); })
.OnSetupConfiguration(builder => builder.AddInMemoryCollection(new Dictionary<string, string>
{
{"Country", "France"}
}))
.BuildAsync();
// Assert
var service = sut.GetService<ClassWithConfiguration>();
service.Should().NotBeNull();
service.Configuration["Country"].Should().Be("France");
}
In this test, we're customizing the configuration by adding an in-memory collection with a key-value pair of "Country" and "France". After building the SUT, we assert that the service of type ClassWithConfiguration
is not null and that the configuration value for "Country" is "France".
Extending SUTForge
SUTForge
is highly extensible, allowing you to use existing extension methods on IServiceCollection
or to write your own extension methods to customize SUTs according to your unique testing requirements. Here's an example:
public static class SutBuilderExtensions
{
public static SutBuilder WithCurrentTime(this SutBuilder builder, DateTime time)
{
return builder.ConfigureServices(services => { services.AddSingleton<ITimeProvider(new DeterministicTimeProvider(time)); });
}
}
In this example, we're writing an extension method that allows us to customize the SUT by adding a singleton service of type ITimeProvider
with an implementation of DeterministicTimeProvider
to control the time of the SUT. This extension method can then be used in our tests as follows:
[Test]
public async Task SUT_can_be_customized()
{
// Arrange
var time = 14.January(2021).At(12, 0, 0).AsUtc(); // Using FluentAssertions
var sut = await SutBuilder.Create
.AddMyApplicationServices() // đź’ˇ Add the application services (the same method you'll use in your Startup class)
.WithCurrentTime(time)
.BuildAsync();
var service = sut.GetRequiredService<ISomeFancyService>();
// Act
var result = service.DoSomething();
// Assert
// Assert something
}
With SUTForge
, writing unit tests becomes a breeze. Embrace the Chicago approach of unit testing and elevate your TDD experience with SUTForge
. Happy testing!
References
- Unit Testing Principles, Practices, and Patterns (Vladimir Khorikov)
- 🚀 TDD, Where Did It All Go Wrong (Ian Cooper)
- Uncle Bob (Robert C. Martin), TDD Harms Architecture https://blog.cleancoder.com/uncle-bob/2017/03/03/TDD-Harms-Architecture.html (On a shift from London to Chicago School of TDD; About fallacy of <class>Test file pattern)
- TDD Revisited - Ian Cooper - NDC London 2021 (on testing “ports&adapters” architecture with Chicago School of TDD)
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 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. |
.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 is compatible. |
.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.Extensions.Configuration (>= 8.0.0)
- Microsoft.Extensions.DependencyInjection (>= 8.0.0)
-
.NETStandard 2.1
- Microsoft.Extensions.Configuration (>= 8.0.0)
- Microsoft.Extensions.DependencyInjection (>= 8.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.