JLib.DataGeneration
0.8.8
Prefix Reserved
dotnet add package JLib.DataGeneration --version 0.8.8
NuGet\Install-Package JLib.DataGeneration -Version 0.8.8
<PackageReference Include="JLib.DataGeneration" Version="0.8.8" />
paket add JLib.DataGeneration --version 0.8.8
#r "nuget: JLib.DataGeneration, 0.8.8"
// Install JLib.DataGeneration as a Cake Addin #addin nuget:?package=JLib.DataGeneration&version=0.8.8 // Install JLib.DataGeneration as a Cake Tool #tool nuget:?package=JLib.DataGeneration&version=0.8.8
Project Description
This library allows you to manage your testing data using packages and simplifies the use of Ids.
The Concept
Persistent IDs
A Data Package can define public Id-Properties which will be automatically set to the same id each time the test is run. The Id can be either a int, Guid, or a derivative of IntValueType or GuidValueType. The int and Guid Ids are persisted in a file stored next to the project file, containing all ids for the entire project. there are two variants of ids: property and named ids.
Property IDs
A property id is defined by adding a property of one of the types noted above to the data package. The getter should be public and the setter must be present and protected. To get the property id of another package, inject the package and access the property.
Runtime IDs
A runtime id is required when Ids are created at runtime, for example when creating a new entity in the database.
To resolve the issue of being unable to resolve this id, an abstraction layer is used.
When running production code, the JLib.DataGeneration.Abstractions
package can be used to create IDs using the IIdGenerator
. It can be added to the service collection via IdGeneratorServiceCollectionExtensions.AddIdGenerator
.
When running tests, another IIdGenerator
will be added automatically when calling DataPackageExtensions.AddDataPackages
.
By injecting the implementation TestingidGenerator
additional method for creating int and string ids are porovided.
This IdGenerator uses the Stacktrace, specifically the identity (class + method name + generic argument count / generic parameters + parameters) of the caller of the TestingIdGenerator
Only the generic type arguments of the method are included in the identity due to technical limitations.
Named IDs
A named id is defined by getting it from the datapackage base class. the name must be unique per dataPackage. NamedIDs should be used when it is not neccessary to reference this entity from another entity like when creating a n:m reference entity. The name of the id should contain the propertynames of the related ids (for example when you create orderItems for the order named 'CompletedOrder', the name should be 'CompletedOrder_Item_1') A named Id can not be accessed from another package.
Manual ID
A manual Id is a id which can be defined at runtime. It is composed of a groupName and a IdName. You MUST NOT select the classname of any DataPackage. Doing so might result in duplicate IDs.
Data Packages
Extentions
References
Requirements
- automapper (if you want to use the recommended typed IDs)
- JLib TypeCache
- references to all used typed ids
- reference to the IntValueType / GuidValueType records
How to
get the identifier of an id value
during debugging, you can simply use the .Info/
Create a class Derived from DataPackage and implement the constructor
using JLib.DataGeneration;
// ...
public class MyDataPackage : DataPackage
{
public MyDataPackage(IDataPackageStore dataPackages) : base(dataPackages)
{
}
}
Get Deterministic Ids
To create deterministic ids, add a public property of type guid
or a derivative of GuidValueType
to your DataPackage
using JLib.DataGeneration;
using JLib.ValueTypes;
// ...
#pragma warning disable CS8602 // Dereference of a possibly null reference.
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
namespace MyNamespace;
#region referenced types
// id valuetype
public record ArticleId(Guid Value) : GuidValueType(Value);
// efcore entity
public class Article
{
[Key]
Guid Id { get; init; }
string Name { get; init; }
}
public class ShopDbContext : DbContext
#endregion
// DataPackage
public class MyDataPackage : DataPackage
{
public ArticleId ArticleId { get; init; }
public MyDataPackage(IDataPackageManager packageManager, MyDbContext dbContext):base(packageManager)
{
dbContext.Articles.Add(new()
{
Id = ArticleId.Value,
Name = GetInfoText(nameof(ArticleId))
});
}
}
public class UnitTest : IDisposable
{
[Fact] public async Task ImportLocationsOnly() => await RunTest(new[] { typeof(NotImportedLocationsDp) });
private readonly CancellationToken _cancellationToken;
private readonly IServiceProvider _provider;
private readonly List<IDisposable> _disposables = new();
private readonly ITypeCache _typeCache;
public UnitTest()
{
var id = Guid.NewGuid();
var exceptions = new ExceptionBuilder("test setup");
var services = new ServiceCollection()
.AddTypeCache(out _typeCache, exceptions,
BackendTestsTypePackage.Instance, JLibDataGenerationTypePackage.Instance)
.AddAutoMapper(p => p.AddProfiles(_typeCache))
.AddDataPackages(_typeCache)
}
}
How it works
start-time
- ServiceCollection.AddDataPackages
- the types are pulled from the typecache
- then they get added to the serviceCollection
- the dataPackageManager is added
- ServiceProvider.IncludeDataPackages
- DataPackageManager.InitState uninitialized ⇒ initializing
- execute delegate to get a list of dataPackages to load
- load said data packages
- data package constructor
- iterate over all properties which contain a id
- pull the id from the IdRegistry
- if the id does not exist, create a new one
- idProperty = pulled id
- data package constructor
- DataPackageManager.InitState initializing ⇒ initialized
- IdRegistry.SaveToFile
classes
- DataPackage
- DataPackageManager
- information about the init state (uninitialized/initializing/initialized)
todo
- speichern von int ids
- persistieren von ids die zur test-laufzeit erstellt werden
- probleme
- die laufzeit dienste brauchen einen weg die IDs zu generieren, werden jedoch als fertiger provider dem DataPackageManager übergeben
- ansatz 1: testIdIdentifier = testklasse+methode+entität+inkrementnummer/entität
- probleme:
- race-conditions
- wird das anlegen einer neuen entität nötig, werden alle folgenden IDs invalide
- dies könnte reduziert werden, indem man nach jedem methodenaufruf einen neuen inkrement-bereich erstellt
- probleme:
- ansatz 2: testIdentifier = callstack
- ressourcen
- probleme:
- extrem anfällig für private anpassungen/umbenennungen von internen methoden
- der callstacktext ändert sich nach debug/release build
- vorteile
- jeder aufruf kann eindeutig zugeordnet werden
- probleme
Evaluieren
- zusammenführen des data providers für services und datenpakete
- vorteile
- würde das problem vom übergeben des ID-generators elegant lösen
- es würde den code vereinfachen
- nachteile
- die initalisierung könnte komplizierter werden, da man nach dem hinzufügen der pakete diese exakt ein mal initalisieren muss
- ServiceCollection.addDataPackages()
- ServiceProvider.LoadDataPackages()
- nach dem laden müssen die neuen IDs gespeichert werden
- die initalisierung könnte komplizierter werden, da man nach dem hinzufügen der pakete diese exakt ein mal initalisieren muss
- ergebnisse
- man könnte sich in den tests die datenpakete injecten.
- man könnte sich den DataPackageStore Injecten
- das nachträgliche Injecten von Paketen, z.B. durch referenzierungen in diensten, könnte zur laufzeit daten hinzufügen und so die Datenbaser intransparenter machen
- Lösung:
- beim instantiieren eines pakets wird geprüft, ob das laden bereits abgeschlossen ist. wenn ja, würde eine exception geworfen werden da tests deterministisch sein sollen, würde die exception auch zuverlässig den test direkt am durchlaufen hindern.
- über reflection prüfen, ob eine klasse die datenpakete unerlaubt nutzt. nachteil: das anfragen von datenpaketen via serviceProvider.GetRequiredService würde nicht gefunden werden
- Lösung:
- das nachträgliche Injecten von Paketen, z.B. durch referenzierungen in diensten, könnte zur laufzeit daten hinzufügen und so die Datenbaser intransparenter machen
- datenpakete können direkt den relevanten service injecten
- alternative
- die IdRegistry wird als singleton vom serviceProvider bereitgestellt und vom packageProvider als singleton-instanz referenziert
- vorteile
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net6.0 is compatible. 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 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 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. |
-
net6.0
- AutoMapper (>= 13.0.1)
- JLib.DataGeneration.Abstractions (>= 0.8.8)
- JLib.DependencyInjection (>= 0.8.8)
- JLib.Reflection (>= 0.8.8)
-
net7.0
- AutoMapper (>= 13.0.1)
- JLib.DataGeneration.Abstractions (>= 0.8.8)
- JLib.DependencyInjection (>= 0.8.8)
- JLib.Reflection (>= 0.8.8)
-
net8.0
- AutoMapper (>= 13.0.1)
- JLib.DataGeneration.Abstractions (>= 0.8.8)
- JLib.DependencyInjection (>= 0.8.8)
- JLib.Reflection (>= 0.8.8)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on JLib.DataGeneration:
Package | Downloads |
---|---|
JLib
Metapackage for all JLIb packages |
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated | |
---|---|---|---|
0.8.8 | 238 | 10/18/2024 | |
0.8.7 | 143 | 10/18/2024 | |
0.8.6 | 82 | 10/17/2024 | |
0.8.5 | 156 | 9/18/2024 | |
0.8.4 | 116 | 9/18/2024 | |
0.8.3 | 118 | 9/11/2024 | |
0.8.1 | 99 | 9/2/2024 | |
0.8.0 | 128 | 8/28/2024 | |
0.7.6 | 112 | 8/27/2024 | |
0.7.5 | 109 | 8/27/2024 | |
0.7.4 | 114 | 8/27/2024 | |
0.7.3 | 111 | 8/27/2024 | |
0.7.1 | 170 | 7/24/2024 | |
0.7.0 | 100 | 7/23/2024 | |
0.6.2 | 352 | 4/10/2024 | |
0.6.1 | 143 | 3/15/2024 | |
0.6.0 | 131 | 3/15/2024 | |
0.5.0 | 128 | 3/14/2024 | |
0.4.5 | 158 | 3/13/2024 | |
0.4.4 | 122 | 3/12/2024 | |
0.4.3 | 132 | 3/12/2024 | |
0.4.2 | 154 | 3/7/2024 | |
0.4.1 | 139 | 3/7/2024 | |
0.4.0 | 163 | 3/6/2024 | |
0.3.0 | 156 | 2/29/2024 | |
0.2.0 | 137 | 2/14/2024 | |
0.1.1 | 140 | 2/6/2024 | |
0.1.0 | 135 | 1/26/2024 |