DRN.Framework.Testing
0.1.0-preview012
Prefix Reserved
See the version list below for details.
dotnet add package DRN.Framework.Testing --version 0.1.0-preview012
NuGet\Install-Package DRN.Framework.Testing -Version 0.1.0-preview012
<PackageReference Include="DRN.Framework.Testing" Version="0.1.0-preview012" />
paket add DRN.Framework.Testing --version 0.1.0-preview012
#r "nuget: DRN.Framework.Testing, 0.1.0-preview012"
// Install DRN.Framework.Testing as a Cake Addin #addin nuget:?package=DRN.Framework.Testing&version=0.1.0-preview012&prerelease // Install DRN.Framework.Testing as a Cake Tool #tool nuget:?package=DRN.Framework.Testing&version=0.1.0-preview012&prerelease
Introduction
DRN.Framework.Testing package provides practical, effective helpers such as resourceful data attributes and test context.
This package enables a new encouraging testing technique called as DTT(Duran's Testing Technique). With DTT, any developer can write clean and hassle-free unit and integration tests without complexity.
Encapsulated Packages
You no longer need to be reference followings in your test project:
- AutoFixture.AutoNSubstitute
- AutoFixture.Xunit2
- DRN.Framework.Utils
- FluentAssertions
- NSubstitute
- xunit
QuickStart: Basics
Here's a basic test demonstration to take your attention and get you started:
[Theory]
[DataInlineContext]
public void DataInlineContextDemonstration(TestContext context, IMockable autoInlinedDependency)
{
context.ServiceCollection.AddApplicationServices();
//Context wraps service provider and automagically replaces actual dependencies with auto inlined dependencies
var dependentService = context.GetRequiredService<DependentService>();
autoInlinedDependency.Max.Returns(int.MaxValue); //dependency is mocked by NSubstitute
dependentService.Max.Should().Be(int.MaxValue); //That is all. It is clean and effective
}
Table of Contents
- Introduction
- TestContext
- DataAttributes
- DebugOnly Tests
- Settings and Data Providers
- Global Usings
- Example Test Project
- Test snippet
- Testing guide and DTT approach
Testing models used in the QuickStart
public static class ApplicationModule //Can be defined in Application Layer or in Hosted App
{
public static void AddApplicationServices(this IServiceCollection serviceCollection)
{
serviceCollection.AddTransient<IMockable, ToBeRemovedService>(); //will be removed by test context because test method requested mocked interface
serviceCollection.AddTransient<DependentService>(); //dependent service uses IMockable and Max property returns dependency's Max value
}
}
public interface IMockable
{
public int Max { get; }
}
public class ToBeRemovedService : IMockable
{
public int Max { get; set; }
}
public class DependentService : IMockable
{
private readonly IMockable _mockable;
public DependentService(IMockable mockable)
{
_mockable = mockable;
}
public int Max => _mockable.Max;
}
QuickStart: Advanced Data Inline
DataInlineContext
will provideTestContext
as first parameter.- Then it will provide inlined values.
- Then it will provide auto inline missing values with AutoFixture.
AutoFixture
will mock any interface requested withNSubstitute
.
/// <param name="context"> Provided by DataInlineContext even if it is not a compile time constant</param>
/// <param name="inlineData">Provided by DataInlineContext</param>
/// <param name="autoInlinedData">DataInlineContext will provide missing data with the help of AutoFixture</param>
/// <param name="autoInlinedMockable">DataInlineContext will provide implementation mocked by NSubstitute</param>
[Theory]
[DataInlineContext(99)]
public void TextContext_Should_Be_Created_From_TestContextData(TestContext context, int inlineData, Guid autoInlinedData, IMockable autoInlinedMockable)
{
inlineData.Should().Be(99);
autoInlinedData.Should().NotBeEmpty(); //guid generated by AutoFixture
autoInlinedMockable.Max.Returns(int.MaxValue); //dependency mocked by NSubstitute
context.ServiceCollection.AddApplicationServices(); //you can add services, modules defined in hosted app, application, infrastructure layer etc..
var serviceProvider = context.BuildServiceProvider(); //appsettings.json added by convention. Context and service provider will be disposed by xunit
serviceProvider.GetService<ToBeRemovedService>().Should().BeNull(); //Service provider behaviour demonstration
var dependentService = serviceProvider.GetRequiredService<DependentService>();
dependentService.Max.Should().Be(int.MaxValue);
}
TestContext
TestContext
has following properties:
- captures values provided to running test method and its method info.
- provides
ServiceCollection
so that to be tested services and dependencies can be added before buildingServiceProvider
. - provides and implements lightweight
ServiceProvider
that contains default logging without any providerServiceProvider
can provide services that depends likeILogger<DefaultService>
- logged data will not be leaked to anywhere since it has no logging provider.
- provides
IConfiguration
andIAppSettings
with SettingsProvider by using convention.- settings.json file can be found in the same folder with test
- settings.json file can be found in the global Settings folder or Settings folder that stays in the test folder
- Make sure file is copied to output directory
- If no settings file is specified while calling
BuildServiceProvider
.appsettings.json
file be searched by convention.
- provides data file contents by using convention.
- data file can be found in the same folder with test
- data file can be found in the global Data folder or Data folder that stays in the test folder
- Make sure file is copied to output directory
ServiceProvider
provides utils provided with DRN.Framework.Utils'UtilsModule
BuildServiceProvider
replaces dependencies that can be replaced with inlined interfaces.ServiceProvider
andTestContext
will be disposed by xunit when test finishes
settings.json
can be put in the same folder that test file belongs. This way providing and isolating test settings is much more easier
[Theory]
[DataInlineContext( "localhost")]
public void TestContext_Should_Add_Settings_Json_To_Configuration(TestContext context, string value)
{
//settings.json file can be found in the same folder with test file, in the global Settings folder or Settings folder that stays in the same folder with test file
context.GetRequiredService<IAppSettings>().GetRequiredSection("AllowedHosts").Value.Should().Be(value);
}
data.txt
can be put in the same folder that test file belongs. This way providing and isolating test data is much more easier
[Theory]
[DataInlineContext("data.txt", "Atatürk")]
[DataInlineContext("alternateData.txt", "Father of Turks")]
public void TestContext_Should_Return_Test_Specific_Data(TestContext context, string dataPath, string data)
{
//data file can be found in the same folder with test file, in the global Data folder or Data folder that stays in the same folder with test file
context.GetData(dataPath).Should().Be(data);
}
Data Attributes
DRN.Framework.Testing provides following data attributes that can provide data to tests:
- DataInlineAutoAttribute
- DataInlineContextAttribute
- DataMemberAutoAttribute
- DataMemberContextAttribute
- DataSelfAutoAttribute
- DataSelfContextAttribute
Following design principle is used for these attributes
- All attributes has data prefix to benefit from autocomplete
- Inline attributes works like xunit
InlineData
except they try to provide missing values with AutoFixture and NSubstitute - Member attributes works like xunit
MemberData
except they try to provide missing values with AutoFixture and NSubstitute - Self attributes needs to be inherited by another class and should call
AddRow
method in constructor to provide data - Context attributes provide
TestContext
as first parameter
Member Attributes
Example usages for member attributes
[Theory]
[DataMemberAuto(nameof(DataMemberAutoData))]
public void AutoMember_Should_Inline_And_Auto_Generate_Missing_Test_Data(int inline, ComplexInline complexInline, Guid autoGenerate, IMockable mock)
{
inline.Should().BeGreaterThan(10);
complexInline.Count.Should().BeLessThan(10);
autoGenerate.Should().NotBeEmpty();
mock.Max.Returns(75);
mock.Max.Should().Be(75);
}
public static IEnumerable<object[]> DataMemberAutoData => new List<object[]>
{
new object[] { 11, new ComplexInline(8) },
new object[] { int.MaxValue, new ComplexInline(-1) }
};
[Theory]
[DataMemberContext(nameof(TestContextInlineMemberData))]
public void TestContextMember_Should_Inline_And_Auto_Generate_Missing_Test_Data(TestContext testContext,
int inline, ComplexInline complexInline, Guid autoGenerate, IMockable mock)
{
testContext.Should().NotBeNull();
testContext.TestMethod.Name.Should().Be(nameof(TestContextMember_Should_Inline_And_Auto_Generate_Missing_Test_Data));
inline.Should().BeGreaterThan(10);
complexInline.Count.Should().BeLessThan(10);
autoGenerate.Should().NotBeEmpty();
mock.Max.Returns(75);
mock.Max.Should().Be(75);
}
public static IEnumerable<object[]> TestContextInlineMemberData => new List<object[]>
{
new object[] { 11, new ComplexInline(8) },
new object[] { int.MaxValue, new ComplexInline(-1) }
};
Self Attributes
Example usages for self attributes
public class DataSelfAutoAttributeTests
{
[Theory]
[DataSelfAutoTestData]
public void AutoClass_Should_Inline_And_Auto_Generate_Missing_Test_Data(int inline, ComplexInline complexInline, Guid autoGenerate, IMockable mock)
{
inline.Should().BeGreaterThan(10);
complexInline.Count.Should().BeLessThan(10);
autoGenerate.Should().NotBeEmpty();
mock.Max.Returns(75);
mock.Max.Should().Be(75);
}
}
public class DataSelfAutoTestData : DataSelfAutoAttribute
{
public DataSelfAutoTestData()
{
AddRow(200, new ComplexInline(9));
AddRow(300, new ComplexInline(int.MinValue));
}
}
public class DataSelfContextAttributeTests
{
[Theory]
[DataSelfContextTestData]
public void TestContextClassData_Should_Inline_And_Auto_Generate_Missing_Test_Data(TestContext testContext,
int inline, ComplexInline complexInline, Guid autoGenerate, IMockable mock)
{
testContext.Should().NotBeNull();
testContext.TestMethod.Name.Should().Be(nameof(TestContextClassData_Should_Inline_And_Auto_Generate_Missing_Test_Data));
inline.Should().BeGreaterThan(98);
complexInline.Count.Should().BeLessThan(1001);
autoGenerate.Should().NotBeEmpty();
mock.Max.Returns(44);
mock.Max.Should().Be(44);
}
}
public class DataSelfContextTestData : DataSelfContextAttribute
{
public DataSelfContextTestData1()
{
AddRow(99,new ComplexInline(100));
AddRow(199,new ComplexInline(1000));
}
}
Inline Attributes
Example usages for inline attributes
[Theory]
[DataInlineAuto(10)]
public void AutoInline_Should_Inline_And_Auto_Generate_Missing_Test_Data(int inline, Guid autoGenerate, IMockable mock)
{
inline.Should().Be(10);
autoGenerate.Should().NotBeEmpty();
mock.Max.Returns(65);
mock.Max.Should().Be(65);
}
[Theory]
[DataInlineContext(99)]
public void TextContext_Should_Be_Created_From_TestContextData(TestContext context, int inlineData, Guid autoInlinedData, IMockable autoInlinedMockable)
{
inlineData.Should().Be(99);
autoInlinedData.Should().NotBeEmpty(); //guid generated by AutoFixture
autoInlinedMockable.Max.Returns(int.MaxValue); //dependency mocked by NSubstitute
context.ServiceCollection.AddApplicationServices(); //you can add services, modules defined in hosted app, application, infrastructure layer etc..
var serviceProvider = context.BuildServiceProvider(); //appsettings.json added by convention. Context and service provider will be disposed by xunit
serviceProvider.GetService<ToBeRemovedService>().Should().BeNull(); //Service provider behaviour demonstration
var dependentService = serviceProvider.GetRequiredService<DependentService>();
dependentService.Max.Should().Be(int.MaxValue);
}
DebugOnly Tests
Following attributes can be used to run test only when the debugger is attached. These attributes does respect the attached debugger, not debug or release configuration.
- FactDebuggerOnly
- TheoryDebuggerOnly
Providers
SettingsProvider
SettingsProvider
gets the settings from Settings folder. Settings file path is relative Settings folder. Settings folder must be created in the root of the test Project. Make sure the settings file is copied to output directory.
[Fact]
public void SettingsProvider_Should_Return_IAppSettings_Instance()
{
var appSettings = SettingsProvider.GetAppSettings();
appSettings.GetRequiredSection("AllowedHosts").Value.Should().Be("*");
appSettings.TryGetSection("Bar", out _).Should().BeTrue();
appSettings.TryGetSection("Foo", out _).Should().BeFalse();
appSettings.GetRequiredConnectionString("Foo").Should().Be("Bar");
appSettings.TryGetConnectionString("Bar", out _).Should().BeFalse();
}
[Fact]
public void SettingsProvider_Should_Return_IConfiguration_Instance()
{
var configuration = SettingsProvider.GetConfiguration("secondaryAppSettings");
configuration.GetRequiredSection("AllowedHosts").Value.Should().Be("*");
configuration.GetSection("Foo").Exists().Should().BeTrue();
configuration.GetSection("Bar").Exists().Should().BeFalse();
configuration.GetConnectionString("Bar").Should().Be("Foo");
}
DataProvider
DataProvider
gets the content of specified data file in the Data folder. Data file path is relative Data folder including file extension. Data folder must be created in the root of the test Project. Make sure the data file is copied to output directory.
[Fact]
public void DataProvider_Should_Return_Data_From_Test_File()
{
DataProvider.Get("Test.txt").Should().Be("Foo");
}
Global Usings
Following global usings can be used in a Usings.cs
file in test projects to reduce line of code in test files
global using Xunit;
global using AutoFixture;
global using AutoFixture.AutoNSubstitute;
global using AutoFixture.Xunit2;
global using DRN.Framework.Utils.Extensions;
global using DRN.Framework.SharedKernel;
global using DRN.Framework.Utils.Settings;
global using DRN.Framework.Utils.DependencyInjection;
global using DRN.Framework.Testing;
global using DRN.Framework.Testing.DataAttributes;
global using DRN.Framework.Testing.Providers;
global using DRN.Framework.Testing.TestAttributes;
global using DRN.Framework.Testing.Contexts;
global using FluentAssertions;
global using Microsoft.Extensions.DependencyInjection;
global using Microsoft.Extensions.DependencyInjection.Extensions;
global using Microsoft.Extensions.Configuration;
global using NSubstitute;
global using System.Reflection;
global using System.IO;
global using System.Linq;
global using System.Collections;
Example Test Project .csproj File
Don't forget to replace DRN.Framework.Testing project reference with its nuget package reference
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\DRN.Framework.Testing\DRN.Framework.Testing.csproj"/>
</ItemGroup>
<ItemGroup>
<None Update="Settings\defaultAppSettings.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="Data\Test.txt">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="Settings\secondaryAppSettings.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
Test snippet
dtt snippet for creating tests with a test context.
[Theory]
[DataInlineContext]
public void $name$(TestContext context)
{
$END$
}
Testing guide and DTT approach
DTT(Duran's Testing Technique) is developed upon following 2 idea:
- Writing a test, providing settings and data to it should be easy, effective and encouraging as much as possible
- A test should test actual usage as much as possible.
DTT with TestContext makes these ideas possible by
- effortlessly providing test data and settings
- effortlessly providing service collection
- effortlessly providing service provider
- effortlessly validating service provider
- being aware of test data and location
With the help of test context, integration tests can be written easily with following styles.
- A data context attribute can provide NSubstituted interfaces and test context automatically replaces actual implementations with mocked interfaces and provides test data.
- Test containers can be used as actual dependencies instead of mocking them.
- With FactDebuggerOnly and TheoryDebuggerOnly attributes, cautiously written tests can use real databases and dependencies to debug production usage.
Commit Info
Author: Duran Serkan KILIÇ
Date: 2023-11-20 23:39:37 +0300
Hash: 304405f6473d50e1aa9609f75ad066aa02ed9471
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | 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. |
-
net8.0
- AutoFixture.AutoNSubstitute (>= 4.18.0)
- AutoFixture.Xunit2 (>= 4.18.0)
- DRN.Framework.Utils (>= 0.1.0-preview012)
- FluentAssertions (>= 6.12.0)
- NSubstitute (>= 5.1.0)
- xunit (>= 2.6.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 |
---|---|---|
0.7.0-preview005 | 67 | 11/27/2024 |
0.7.0-preview004 | 66 | 11/23/2024 |
0.7.0-preview003 | 67 | 11/20/2024 |
0.7.0-preview002 | 66 | 11/17/2024 |
0.7.0-preview001 | 73 | 11/14/2024 |
0.6.0 | 97 | 11/10/2024 |
0.6.0-preview002 | 73 | 11/10/2024 |
0.6.0-preview001 | 75 | 11/10/2024 |
0.5.1-preview002 | 82 | 9/30/2024 |
0.5.1-preview001 | 82 | 9/22/2024 |
0.5.0 | 109 | 8/30/2024 |
0.5.0-preview011 | 71 | 8/30/2024 |
0.5.0-preview010 | 110 | 8/25/2024 |
0.5.0-preview009 | 96 | 8/8/2024 |
0.5.0-preview008 | 80 | 8/7/2024 |
0.5.0-preview007 | 68 | 8/2/2024 |
0.5.0-preview006 | 62 | 7/30/2024 |
0.5.0-preview005 | 89 | 7/27/2024 |
0.5.0-preview004 | 86 | 7/15/2024 |
0.5.0-preview003 | 102 | 6/6/2024 |
0.5.0-preview002 | 102 | 6/5/2024 |
0.5.0-preview001 | 103 | 6/4/2024 |
0.4.0 | 101 | 5/19/2024 |
0.4.0-preview006 | 72 | 5/19/2024 |
0.4.0-preview005 | 95 | 5/12/2024 |
0.4.0-preview004 | 94 | 5/12/2024 |
0.4.0-preview003 | 100 | 5/11/2024 |
0.4.0-preview002 | 100 | 5/8/2024 |
0.4.0-preview001 | 103 | 5/5/2024 |
0.3.1-preview001 | 97 | 4/26/2024 |
0.3.0 | 105 | 4/23/2024 |
0.3.0-preview002 | 80 | 4/23/2024 |
0.3.0-preview001 | 93 | 4/23/2024 |
0.2.2-preview010 | 103 | 4/11/2024 |
0.2.2-preview009 | 102 | 3/18/2024 |
0.2.2-preview008 | 107 | 3/18/2024 |
0.2.2-preview007 | 97 | 3/16/2024 |
0.2.2-preview006 | 81 | 3/11/2024 |
0.2.2-preview005 | 92 | 3/10/2024 |
0.2.2-preview004 | 90 | 3/10/2024 |
0.2.2-preview003 | 125 | 1/22/2024 |
0.2.2-preview002 | 103 | 1/18/2024 |
0.2.2-preview001 | 113 | 1/14/2024 |
0.2.1 | 184 | 1/7/2024 |
0.2.0 | 149 | 12/31/2023 |
0.2.0-preview009 | 120 | 12/31/2023 |
0.2.0-preview008 | 111 | 12/30/2023 |
0.2.0-preview007 | 125 | 12/28/2023 |
0.2.0-preview006 | 116 | 12/27/2023 |
0.2.0-preview005 | 119 | 12/25/2023 |
0.2.0-preview004 | 109 | 12/23/2023 |
0.2.0-preview003 | 128 | 12/20/2023 |
0.2.0-preview002 | 115 | 12/19/2023 |
0.2.0-preview001 | 112 | 12/18/2023 |
0.1.0 | 187 | 11/26/2023 |
0.1.0-preview013 | 129 | 11/26/2023 |
0.1.0-preview012 | 109 | 11/20/2023 |
0.1.0-preview011 | 122 | 11/19/2023 |
0.1.0-preview010 | 143 | 10/30/2023 |
0.1.0-preview009 | 99 | 10/29/2023 |
0.1.0-preview008 | 118 | 10/27/2023 |
0.1.0-preview007 | 112 | 10/11/2023 |
0.1.0-preview006 | 112 | 10/9/2023 |
0.1.0-preview005 | 115 | 10/8/2023 |
0.1.0-preview004 | 114 | 10/8/2023 |
0.1.0-preview003 | 100 | 10/3/2023 |
0.1.0-preview002 | 127 | 10/3/2023 |
0.1.0-preview001 | 130 | 10/2/2023 |
Not every version includes changes, features or bug fixes. This project can increment version to keep consistency with other DRN.Framework projects.
## Version 0.1.0
### Breaking Changes
### New Features
* TestContext added
* FactDebuggerOnly and TheoryDebuggerOnly test attributes added
* Following data attributes added:
* DataInlineAutoAttribute
* DataInlineContextAttribute
* DataMemberAutoAttribute
* DataMemberContextAttribute
* DataSelfAutoAttribute
* DataSelfContextAttribute
* SettingsProvider added
* DataProvider added
### Bug Fixes
## Commit Info
Author: Duran Serkan KILIÇ
Date: 2023-11-20 23:39:37 +0300
Hash: 304405f6473d50e1aa9609f75ad066aa02ed9471