TryAtSoftware.CleanTests
1.0.0-alpha.7
Prefix Reserved
See the version list below for details.
dotnet add package TryAtSoftware.CleanTests --version 1.0.0-alpha.7
NuGet\Install-Package TryAtSoftware.CleanTests -Version 1.0.0-alpha.7
<PackageReference Include="TryAtSoftware.CleanTests" Version="1.0.0-alpha.7" />
paket add TryAtSoftware.CleanTests --version 1.0.0-alpha.7
#r "nuget: TryAtSoftware.CleanTests, 1.0.0-alpha.7"
// Install TryAtSoftware.CleanTests as a Cake Addin #addin nuget:?package=TryAtSoftware.CleanTests&version=1.0.0-alpha.7&prerelease // Install TryAtSoftware.CleanTests as a Cake Tool #tool nuget:?package=TryAtSoftware.CleanTests&version=1.0.0-alpha.7&prerelease
About the project
TryAtSoftware.CleanTests
is a library that should simplify the process of automated testing for complex setups.
The repeating pattern that we could discover in some advanced projects is that whenever more features are added or old ones are being refactored, adding new tests or modifying existing ones could be a tough challenge.
One of the private projects that uses our library has a lot of polymorphic components and every concrete implementation has a totally different logic.
There were two main test assemblies - an old one (let's call it Standard
for brevity) where standard patterns for testing were applied and a new one (let's call it Clean
for brevity) integrating TryAtSoftware.CleanTests
.
We could easily compare the two approaches as we were working on them independently.
In the past, there were multiple test assemblies with more than 1500 tests that were executing for over 10 minutes.
However, TryAtSoftware.CleanTests
was integrated in a different assembly and we could easily compare the two approaches.
Moreover, if the more components and logical branches there are in the code, the less scenarios are covered.
After finalizing the integration, we could notice the following:
Criteria | Standard test assembly | Clean test assembly |
---|---|---|
Number of written test | > 1500 | < 100 |
Number of test cases | < 1700 | > 15000 |
Execution time (approximated) | 15 minutes | 20-25 minutes |
Code coverage (approximated) | < 20% | > 80% |
Number of found bugs | 0 | > 20 |
As you can see, this is quite big of a difference!
With much less effort we managed to achieve unthinkable results.
If we had to stick to the Standard
testing approach in order to increase the code coverage and amount of test cases and optimize the performance, we had to write a lot of code.
And if we had to do the same for every new functionality that is coming, that would slow down the software development process significantly.
TryAtSoftware.CleanTests
gave us an alternative approach of automatic testing that not only improved the quality of the product but also saved us a lot of time that we could invest in adding more and more features.
The main goals that have been accomplished are:
- Automatic generation of test cases using all proper variations of registered
clean utilities
- Every
clean utility
can define external demands that represent conditions about what otherclean utilities
should be present within a variation in order to generate a test case with it - Every
clean utility
can depend internally on otherclean utilities
- Every
clean utility
can define internal demands that represent conditions about whatclean utilities
should be injected upon initialization - Global and local
clean utilities
- localclean utilities
are instantiated for every test case; globalclean utilities
are instantiated only once and can be used to share common context between similar test cases - Parallel execution of tests cases
About us
Try At Software
is a software development company based in Bulgaria. We are mainly using dotnet
technologies (C#
, ASP.NET Core
, Entity Framework Core
, etc.) and our main idea is to provide a set of tools that can simplify the majority of work a developer does on a daily basis.
Getting started
Installing the package
Before creating any equalization profiles, you need to install the package.
The simplest way to do this is to either use the NuGet package manager
, or the dotnet CLI
.
Using the NuGet package manager
console within Visual Studio, you can install the package using the following command:
Install-Package TryAtSoftware.CleanTests
Or using the dotnet CLI
from a terminal window:
dotnet add package TryAtSoftware.CleanTests
Configurations
In order to use the features of this library, there is one mandatory step that must be done.
Your test assembly should be decorated with an appropriate attribute that will define which test framework should be used for the execution of test cases.
Add the following line anywhere in your project (most likely this is done within an AssemblyInfo.cs
file):
[assembly: Xunit.TestFramework("TryAtSoftware.CleanTests.Core.XUnit.CleanTestFramework", "TryAtSoftware.CleanTests.Core")]
Modifying behavior
Additionally, you can modify the behavior of the clean tests
execution framework using the ConfigureCleanTestsFramework
attribute.
There is a list of the parameters that can be controlled:
UseTraits
- Indicates whether or not to add traits to each generated test case. Enabling this may have performance impact over the discovery process when dealing with a big amount of tests because of the amount of data stored in these traits. The default value isfalse
.
Example:
[assembly: TryAtSoftware.CleanTests.Core.Attributes.ConfigureCleanTestsFramework(UseTraits = true)]
What are the clean utilities
?
The clean utility
is a key component for our library. Every clean utility
has a category
and a name
that are required.
One test may require many clean utilities
and whenever there are two or more utilities from the same category that can be used for its execution, then a test case will be generated for each possible variation of utilities.
Every clean utility
can me marked as local
or global
.
Local clean utilities
will be instantiated at least once for every test case requiring their participation.
Global clean utilities
will be instantiated only once for all test cases sharing a common context.
Moreover, every clean utility
can optionally define its own characteristics.
These characteristics can be used to filter out on some basis the utilities that we want to use when generating the cases for a given test.
They do often correspond to essential segments of the requested component's behavior.
We use demands
to make sure that the capabilities our test needs are present for the resolved utilities used to execute the test.
In order to use a type as a clean utility
, it should be marked with the CleanUtility
attribute that accepts category
, name
and characteristics
.
You can also explicitly set a value to the IsGlobal
flag.
Example:
[CleanUtility(Categories.Writers, "Console writer", Characteristics.UsesConsole, Characteristics.ActiveWriter)]
public class ConsoleWriter : IWriter
{
public void Write(string text) => Console.WriteLine(text);
}
[CleanUtility(Categories.Writers, "File writer", Characteristics.UsesFile, Characteristics.ActiveWriter)]
public class FileWriter : IWriter
{
public void Write(string text) => File.WriteAllText("C:/path_to_document", text);
}
[CleanUtility(Categories.Writers, "Fake writer")]
public class FakeWriter : IWriter
{
public void Write(string text) { /* Do nothing */ }
}
All clean utilities
should be located within the test assembly.
If this is not possible, the test assembly should be explicitly decorated with an attribute denoting where the shared clean utilities
are defined.
[assembly: TryAtSoftware.CleanTests.Core.Attributes.SharesUtilitiesWith("Assembly.With.Shared.CleanUtilities")]
External demands
Every clean utility
can define external demands
throughout the ExternalDemands
attribute. With its help for each category
a set of demanded characteristics
can be defined.
These demanded characteristics will alter the way variations of clean utilities
are generated - all external demands should be satisfied for all utilities participating in the variation.
Example:
[CleanUtility(Categories.Readers, "Console reader")]
[ExternalDemands(Categories.Writers, Characteristics.UsesConsole)]
public class ConsoleReader : IReader
{
public string Read() => Console.ReadLine();
}
Internal requirements and demands
Every clean utility
can depend on other clean utiliites
. This relationship can be modelled throughout the WithRequirements
attribute.
When such clean utility
participates in a variation, that same variation will be reused as many times as the number of possible instantiation procedures there are (according to the registered clean utilities
of the required categories).
Moreover, internal demands
can be applied (throughout the InternalDemands
attribute) to filter out the dependent clean utilities
according to a predefined set of characteristics.
Example:
[CleanUtility(Categories.Engines, "Default engine")]
[WithRequirements(Categories.Readers, Categories.Writers)]
[InternalDemands(Categories.Writers, Characteristics.ActiveWriter)]
public class Engine : IEngine
{
private readonly IReader _reader;
private readonly IWriter _writer;
public Engine(IReader reader, IWriter writer)
{
this._reader = reader ?? throw new ArgumentNullException(nameof(reader));
this._writer = writer ?? throw new ArgumentNullException(nameof(writer));
}
/* further implementation of the `IEngine` interface... */
}
How to use clean tests?
This library is built atop XUnit so if you are familiar with the way this framework operates, you are most likely ready to use clean tests
.
There are only two requirements for this:
- The test should be marked with either
CleanFact
(instead ofFact
) orCleanTheory
(instead ofTheory
).
You can still use tests that are marked with other attributes, however, they will be executed as standard tests and will have none of the behavior clean tests can benefit from.
- The type containing the requested test should implement the
ICleanTest
interface. We suggest reusing the abstractCleanTest
that we have exposed as it will make accessing instances of the registeredclean utilites
easier and you will not have to think about various internal processes that should be handled.
Clean tests can define requirements
representing the set of categories
for which clean utilities
should be provided.
The WithRequirements
attribute can be used in order to achieve that.
Clean tests can also define demands
to filter out only a specific subset of the clean utilities
that can be used for the generation of test cases.
The TestDemands
attribute can be used in order to achieve that - for each category
a set of demanded characteristics
can be defined.
Example:
[CleanFact]
[WithRequirements(Categories.Writers)]
[TestDemands(Categories.Writers, Characteristics.ActiveWriter)]
public void WriteShouldSucceed()
{
IWriter writer = this.GetService<IWriter>();
writer.Write("Some text");
}
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 | netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
.NET Standard | netstandard2.1 is compatible. |
MonoAndroid | monoandroid was computed. |
MonoMac | monomac was computed. |
MonoTouch | monotouch was computed. |
Tizen | 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.1
- Microsoft.Extensions.DependencyInjection (>= 7.0.0)
- System.Threading.Tasks.Dataflow (>= 7.0.0)
- TryAtSoftware.Extensions.Collections (>= 1.1.0)
- TryAtSoftware.Extensions.Reflection (>= 1.1.0)
- xunit (>= 2.4.2)
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 |
---|---|---|
1.1.0-alpha.2 | 87 | 8/9/2024 |
1.1.0-alpha.1 | 154 | 8/8/2023 |
1.0.2 | 183 | 6/19/2023 |
1.0.1 | 137 | 6/16/2023 |
1.0.0 | 100 | 6/14/2023 |
1.0.0-alpha.14 | 81 | 6/3/2023 |
1.0.0-alpha.13 | 79 | 5/24/2023 |
1.0.0-alpha.12 | 115 | 4/25/2023 |
1.0.0-alpha.11 | 80 | 4/22/2023 |
1.0.0-alpha.10 | 83 | 4/13/2023 |
1.0.0-alpha.9 | 91 | 3/29/2023 |
1.0.0-alpha.8 | 108 | 3/10/2023 |
1.0.0-alpha.7 | 97 | 3/6/2023 |
1.0.0-alpha.6 | 124 | 2/15/2023 |
1.0.0-alpha.5 | 100 | 2/9/2023 |
1.0.0-alpha.4 | 110 | 2/2/2023 |
1.0.0-alpha.3 | 98 | 2/2/2023 |
1.0.0-alpha.2 | 101 | 2/1/2023 |
1.0.0-alpha.1 | 98 | 2/1/2023 |