A robust, flexible, extensible, pure .NET framework to facilitate dynamic data-driven testing. - Test data conversion, management and provisioning - Type-safe and thread-safe support for *MSTest*, *NUnit*, *xUnit*, *xUnit.v3* - Modular design, abstractions and ready-to-use integrations - Portable data sources - Traceable descriptive display names
$ dotnet add package CsabaDu.DynamicTestData🎯 A robust, flexible, extensible, pure .NET framework to facilitate dynamic data-driven testing.
This README contains the base info and the current version related notes.
Visit the Wiki for full documentation, including
(Version 2.0.0-beta Foreword)
CsabaDu.DynamicTestData has been reorganized from a single monolithic package into a set of focused, aligned modules (NuGet packages) while keeping a clean, consistent namespace hierarchy under CsabaDu.DynamicTestData.*. Modules are deployable package boundaries; namespaces are logical organization inside those packages. The new layout reduces transitive dependencies, clarifies responsibilities, and makes it easier for developers to adopt only what they need.
See the Segregated Architecture Diagram for a visual overview of module and namespace boundaries and dependencies:
(package: CsabaDu.DynamicTestData.Core)
Foundation layer with essential contract surface, DTOs, stateless encoding helpers and strategy definitions.
See CsabaDu.DynamicTestData.Core README.
(package: CsabaDu.DynamicTestData.Lite)
Lightweight runtime helpers for manual-enumerable-style data sources - depends on Core. See CsabaDu.DynamicTestData.Lite README.
(package: CsabaDu.DynamicTestData)
Complete runtime and convenience surface that provides all concrete implementations, builders, holders, rows, strategies, and adapters, composed on Core and Lite.
Namespaces and highlights:
CsabaDu.DynamicTestData.DataStrategyTypes
DataStrategy concrete implementation (flyweight pattern):
DataStrategy.cs
DataStrategy: sealed recordCsabaDu.DynamicTestData.TestDataRows.Interfaces
Data wrapper and converter row contracts (Composite component interfaces:
ITestDataRow.cs
ITestDataRow: interfaceITestDataRow<TRow>: interfaceITestDataRow<TRow, TTestData>: interfaceINamedTestDataRow.cs
INamedTestDataRow: interfaceCsabaDu.DynamicTestData.TestDataRows
Test data row implementations (Composite leaves):
TestDataRow.cs
TestDataRow<TRow>: abstract classTestDataRow<TRow, TTestData>: abstract classNamedTestDataRow.cs
NamedTestDataRow<TRow, TTestData>: abstract classObjectArrayRow.cs
ObjectArrayRow<TTestData>: classCsabaDu.DynamicTestData.DataRowHolders.Interfaces
Data row holder contracts (Composite interfaces):
IDataRowHolder.cs
IDataRowHolder: interfaceIDataRowHolder<TRow>: interfaceIDataRowHolder<TRow, TTestData>: interfaceINamedDataRowHolder.cs
INamedDataRowHolder<TRow, TTestData>: interfaceCollection contracts:
ITestDataRows.cs
ITestDataRows: interfaceIRows.cs
IRows: interfaceINamedRows.cs
INamedRows: interfaceStrongly typed addition contract:
IAddTestData.cs
IAddTestData<TTestData>: interfaceComponent factory method contract:
ITestDataRowFactory.cs
ITestDataRowFactory<TRow, TTestData>: interfaceCsabaDu.DynamicTestData.DataRowHolders
Data row holder implementations (Concrete composites):
DataRowHolder.cs
DataRowHolder<TRow>: abstract classDataRowHolder<TRow, TTestData>: abstract classNamedDataRowHolder.cs
NamedDataRowHolder<TRow, TTestData>: abstract classObjectArrayRowHolder.cs
ObjectArrayRowHolder<TTestData>: classCsabaDu.DynamicTestData.DynamicDataRowSources
Managed data row sources:
DynamicDataSource.cs
DynamicDataRowSource<TDataRowHolder, TRow> : abstract classDynamicDataRowSource<TRow> : abstract classDynamicNamedDataSource.cs
DynamicNamedDataRowSource<TRow> : abstract classDynamicObjectArraySource.cs
DynamicObjectArrayRowSource : abstract classDynamicExpectedObjectArraySource.cs
DynamicExpectedObjectArrayRowSource : abstract classWhen to use:
Dependencies: CsabaDu.DynamicTestData.Lite (>= 2.1.0-beta)
The CsabaDu.DynamicTestData framework has undergone a major transformation in version 2.0.0-beta, introducing a wide range of enhancements while preserving its original foundation.
The core components from the 1.x.x series — particularly the TestDataTypes.* namespaces and the DynamicDataSource class — remain central to the framework. However, even these familiar types have received small but breaking changes to align with the new architecture.
This release introduces powerful new capabilities:
These features make the framework easier to use, more adaptable to diverse testing needs, and better suited for integration with MSTest, NUnit, xUnit, and beyond. The newly introduced interfaces and abstract classes are designed for extensibility, allowing developers to support custom types and framework-specific features while staying aligned with the CsabaDu.DynamicTestData ecosystem.
The architecture is clean, the codebase is modular, and many features have been partially tested. The documentation provides detailed insights into the design, types, and usage patterns. However, this version is still considered beta due to:
Final Notes
This version is beta, meaning:
If you're upgrading from version 2.0.x-beta to 2.1.0-beta, please note the following breaking changes:
...RowSource (e.g., DynamicDataRowSource<TRow>, DynamicObjectArrayRowSource, etc.) have been moved from the DynamicDataSources namespace to the new DynamicDataRowSources namespace. Once you have inherited from any of these classes in your data source implementations, you need to update the using directives accordingly.If you're upgrading from an earlier version of CsabaDu.DynamicTestData, please note:
⚠️ Compatibility is broken, even though the structure of your data source methods may remain similar to current requirements.
To ensure compatibility with the latest version (incomplete list):
ArgsCode moved from DynamicDataSources → StaticsTestData.TestCase → TestData.TestCaseNameTestDataToArgs(...) → TestDataToParams(...)DynamicDataSource constructor now requires two parameters: ArgsCode argsCode, PropsCode propsCodebool? withExpected was used → replace with PropsCode propsCodeA full migration guide is on the way.
In the meantime, feel free to reach out or open an issue if you need help.
This is a beta release introducing breaking changes, new features, and architectural enhancements to the
CsabaDu.DynamicTestDatalibrary. These updates improve usability, flexibility, and extensibility across supported test frameworks.
Namespaces moved with full contents to CsabaDu.DynamicTestData.Core package:
StaticsTestDataTypes.InterfacesTestDataTypesNamespaces moved with full contents to CsabaDu.DynamicTestData.Lite package:
DataStrategyTypes.InterfacesNamespaces moved with partial contents to CsabaDu.DynamicTestData.Lite package:
DynamicDataSources
New package dependency :
CsabaDu.DynamicTestData.Lite (including CsabaDu.DynamicTestData.Core as transitive dependency)Classes muved from DynamicDataSources namespace to new DynamicDataRowSources namespace:
DynamicDataRowSource<TDataRowHolder, TRow>DynamicDataRowSource<TRow>DynamicNamedDataRowSource<TRow>DynamicObjectArrayRowSourceDynamicExpectedObjectArrayRowSourceThis is a beta release introducing breaking changes, new features, and architectural enhancements to the
CsabaDu.DynamicTestDatalibrary. These updates improve usability, flexibility, and extensibility across supported test frameworks.
This section summarizes changes to existing types introduced in this release. For details of the updated members and their current definitions, see the Types section.
Statics| Type | Modified Member | Change | Current Member |
|---|---|---|---|
Extensions | static PropsCode Defined(this PropsCode, string) | New | Extensions.Defined(...) |
static InvalidEnumArgumentException GetInvalidEnumArgumentException(this PropsCode, string) | New | Extensions.GetInvalidEnumArgumentException(...) |
TestDataTypes.Interfaces| Type | Modified Member | Change | Current Member |
|---|---|---|---|
ITestCaseName | string TestCase { get; } | Shifted to ITestData<TResult> and renamed | ITestData<TResult>.TestCaseName |
string GetTestCaseName() | New member | ITestCaseName.GetTestCaseName() | |
ITestData | string ExitMode { get; }, string Result { get; }, object?[] PropertiesToArgs(bool) | Cancelled | - |
object?[] ToParams(ArgsCode, bool) | Signature changed: bool → PropsCode | ToParams(ArgsCode, PropsCode) |
TestDataTypes| Type | Modified Member | Change | Current Member |
|---|---|---|---|
TestData | string TestCase { get; } | Renamed to TestCaseName | TestData.TestCaseName |
ExitMode, Result, PropertiesToArgs(bool) | Cancelled | - | |
ToParams(ArgsCode, bool) | Signature changed: bool → PropsCode | ToParams(ArgsCode, PropsCode) |
DynamicDataSources| Type | Modified Member | Change | Current Member |
|---|---|---|---|
ArgsCode | - | Shifted to namespace Statics | Statics.ArgsCode |
DynamicDataSource | ArgsCode, PropsCode | Refactored and exposed via IDataStrategy | IDataStrategy.ArgsCode, PropsCode |
DynamicDataSource(ArgsCode) (constructor) | Signature changed (PropsCode added) | DynamicDataSource(ArgsCode, PropsCode | |
GetDisplayName(...), TestDataToParams(...) | Shifted to TestDataFactory, signature changed | TestDataFactory.GetDisplayName(...), TestDataToParams(...) | |
OptionalToArgs(...), WithOptionalArgsCode(...) | Cancelled / Refactored | - / WithOptionalArgsCode<T>(...) | |
TestDataToArgs<T...>, TestDataReturnsToArgs<T...>, TestDataThrowsToArgs<T...> | Renamed, made protected, non-static | TestDataToParams<T...>, TestDataReturnsToParam<T...>, TestDataThrowsToParam<T...> |
This section lists newly introduced namespaces and types. For full details, see the Types section.
New Types
Statics
enum PropsCodeTestDataTypes
static TestDataFactoryDynamicDataSources
Base classes
DynamicDataSource<TDataHolder>DynamicDataRowSource<TDataRowHolder, TRow>DynamicDataRowSource<TRow>Specialized base classes
DynamicObjectArraySourceDynamicObjectArrayRowSourceDynamicExpectedObjectArrayRowSourceDynamicNamedDataRowSource<TRow>- New namespaces -
DataStrategyTypes.Interfaces
IDataStrategyDataStrategyTypes
sealed record DataStrategyTestDataRows.Interfaces
ITestDataRow, ITestDataRow<TRow>, ITestDataRow<TRow, TTestData>INamedTestDataRow<TRow>TestDataRows
Base classes
TestDataRow<TRow>TestDataRow<TRow, TTestData>Concrete implementation
ObjectArrayRow<TTestData>DataRowHolders.Interfaces
IDataRowHolder, IDataRowHolder<TRow>, IDataRowHolder<TRow, TTestData>ITestDataRowFactory<TRow, TTestData>IAddTestData<TTestData>ITestDataRowsIRows<TRow>, INamedRows<TRow>INamedDataRowHolder<TRow>DataRowHolders
Base classes
DataRowHolder<TRow>DataRowHolder<TRow, TTestData>Specialized base class
NamedDataRowHolder<TRow, TTestData>Concrete implementation
ObjectArrayRowHolder<TTestData>DataRowHolder<TRow>: Constructors removed:
private protected DataRowHolder(ITestData, IDataStrategy)private protected DataRowHolder(IDataRowHolder, IDataStrategy)DataRowHolder<TRow>: Constructor DataRowHolder(IDataStrategy) made primary consructor.DataRowHolder<TRow, TTestData>: constructors inherit from DataRowHolder<TRow> primary constructor.DynamicDataSource<TDataHolder>: Methods removed (shifted to DynamicDataRowSource<TDataRowHolder>):
protected void Add<T1, T2, ..., T9>(string, string expected, T1?, T2?, ..., T9?),protected void AddReturns<TStruct, T1, T2, ..., T9>(string, string expected, T1?, T2?, ..., T9?),protected void AddThrows<TException, T1, T2, ..., T9>(string, string expected, T1?, T2?, ..., T9?),protected abstract void Add<TTestData>(TTestData),protected abstract void InitDataHolder<TTestData>(TTestData).DynamicDataRowSource<TDataRowHolder>: Methods added (shifted from DynamicDataSource<TDataHolder>):
protected void Add<T1, T2, ..., T9>(string, string expected, T1?, T2?, ..., T9?),protected void AddReturns<TStruct, T1, T2, ..., T9>(string, string expected, T1?, T2?, ..., T9?),protected void AddThrows<TException, T1, T2, ..., T9>(string, string expected, T1?, T2?, ..., T9?),protected abstract void InitDataHolder<TTestData>(TTestData).DynamicDataRowSource<TDataRowHolder>: :
protected override void Add<TTestData>(TTestData) made virtual.DynamicDataSource<TDataHolder>:
protected abstract void Add<TTestData>(TTestData)DynamicDataRowSource<TDataRowHolder>:
protected abstract void Add<TTestData>(TTestData) made virtual.DynamicDataSources.DynamicExpectedObjectArraySource(ArgsCode argsCode) : DynamicObjectArraySource(argsCode, PropsCode.Expected) abstract class added to simplify creating data sources without testcase name when using ArgsCode.Properies.TestDataRows.NamedTestDataRow<TRow, TTestData> abstract class added.ITestCase : IEquatable<ITestCase> added to segregate the string TestCase property of the inherited ITestData interface, and to make the equality of two ITestCase instances comparable, based on their TestCase property.static object?[] TestDataToParams([NotNull] ITestData testData, ArgsCode argsCode, bool withExpected, out string testCaseName) method added to the DynamicDataSource class to null-check the ITestData testData parameter and get the value of its string TestCase property as out-parameter.TestData.PropertiesToArgs(TestData?, bool) refactored.ITestCase interface renamed to ITestCaseName to avoid interference with interfaces of other frameworks having ITestCase named interface.ITestCase interface in your code yet, you should update these names for compatibility purposes.object?[] ToParams(ArgsCode argsCode, bool withExpected) method added to the ITestData interface to simpplify converting the TestData instance to a test framework defined test data type.IExpected interface with object GetExpected() method, which is inherited by ITestDataReturns and ITestDataThrows interfaces to enhance extensibility with accessing the Expected property value of the derived generic TestDataReturns<> or TestDataThrows<> instances from the non-generic marker interface type.DynamicDataSource.GetDisplayName(string testMethodName, object?[] args) extended to parameter args.DynamicDataSource.GetDisplayName(string testMethodName, object?[] args) method simplified.PropertiesToArgs method added to the ITestData interface to generate an object array with the test parameters only.struct constraint for the TStruct type parameter of ITestDataReturns<TStruct> instances.ITestDataReturns and ITestDataThrows base marker interfaces.DynamicDataSource Class section corrected.TestData refactored.WithOptionalArgsCode<> methods to the DynamicDataSource class to support the extension of using the optional ArgsCode? parameter in the derived data source classes.OptionalToArgs method to call a WithOptionalArgsCode<> method.protected DynamicDataSource.tempArgsCode back to private DynamicDataSource._tempArgsCode.WithOptionalArgsCode<> methods in the How it Works section.TestData refactored: ExitMode and Result properties are initialized in the constructor signature.TestDataReturns and TestDataThrows follow this change.DynamicDataSource Class section corrected.OptionalToArgs method added to the DynamicDataSource class.DisposableMemento private class added to the DynamicDataSource class.ArgsCode property behavior of the DynamicDataSource class changed.private DynamicDataSource._tempArgsCode to protected DynamicDataSource.tempArgsCode, to allow for easier extension of the DynamicDataSource class.CsabaDu.DynamicTestData framework.ITestData generic interface types, TestData record types, DynamicDataSource base class, and ArgsCode enum.This project is licensed under the MIT License. See the License file for details.
For any questions or inquiries, please contact CsabaDu.
Can I install IXunitSerializable or IXunitSerializer (xUnit.v3) interfaces to support using TestData types in xUnit tests?
No, you cannot install these interfaces because TestData types are open-generic ones, and don't have parameterless constructors. Although, you can generate object array of xUnit-serializable parameters to use them in TheoryData type data sources. Besides, if your tests don't have to comply with xUnit-serializability, you can use TestData types in xUnit tests well.
Can I use the earlier implemented data source and test classes with version 1.1.0 ?
Yes, you can seamlessly use the already installed classes with the upgraded v1.1.0 of CsabaDu.DynamicTestData without the need of any change in your code. Besides, you can easily modify those to enjoy the benefits of the flexibility of this version.