Optivem.Testing
1.1.9
dotnet add package Optivem.Testing --version 1.1.9
NuGet\Install-Package Optivem.Testing -Version 1.1.9
<PackageReference Include="Optivem.Testing" Version="1.1.9" />
<PackageVersion Include="Optivem.Testing" Version="1.1.9" />
<PackageReference Include="Optivem.Testing" />
paket add Optivem.Testing --version 1.1.9
#r "nuget: Optivem.Testing, 1.1.9"
#:package Optivem.Testing@1.1.9
#addin nuget:?package=Optivem.Testing&version=1.1.9
#tool nuget:?package=Optivem.Testing&version=1.1.9
Optivem Testing (.NET)
Optivem.Testing is an xUnit extension for channel-based data-driven testing. It enables you to run the same tests across multiple channels (UI, API, etc.) with automatic Cartesian product generation and test isolation markers.
Features
- Channel-Based Testing - Run tests across multiple channels (UI, API, etc.)
- Cartesian Product Generation - Automatically combine channels with test data
- Flexible Data Sources - Supports inline data, class data, and member data
- Test Isolation - Mark tests that require isolation (
[Isolated]) - .NET 8+ - Modern .NET support
Installation
dotnet add package Optivem.Testing
Quick Start
Basic Channel Testing
Run the same test across multiple channels:
[Theory]
[ChannelData("UI", "API")]
public void CreateOrder_ShouldSucceed(Channel channel)
{
// Arrange
var order = new Order { ProductId = "P1", Quantity = 1 };
// Act
var result = channel.Type == "UI"
? CreateOrderViaUI(order)
: CreateOrderViaAPI(order);
// Assert
result.Success.ShouldBeTrue();
}
// Generates 2 tests: CreateOrder_ShouldSucceed(UI), CreateOrder_ShouldSucceed(API)
Channel + Inline Data (Cartesian Product)
Combine channels with test data for comprehensive coverage:
[Theory]
[ChannelData("UI", "API")]
[ChannelInlineData("", "Country must not be empty")]
[ChannelInlineData(" ", "Country must not be empty")]
[ChannelInlineData("123", "Country must contain only letters")]
public void CreateOrder_InvalidCountry_ShouldFail(
Channel channel,
string country,
string expectedError)
{
// Test implementation validates error message
}
// Generates 6 tests: 2 channels x 3 data rows
Channel + Class Data
Use a class to provide test data:
public class InvalidCountryData : IEnumerable<object[]>
{
public IEnumerator<object[]> GetEnumerator()
{
yield return new object[] { "", "Country must not be empty" };
yield return new object[] { " ", "Country must not be empty" };
yield return new object[] { "123", "Country must contain only letters" };
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
[Theory]
[ChannelData("UI", "API")]
[ChannelClassData(typeof(InvalidCountryData))]
public void CreateOrder_InvalidCountry_ShouldFail(
Channel channel,
string country,
string expectedError)
{
// Test implementation
}
Channel + Member Data
Use a method or property to provide test data:
public static IEnumerable<object[]> GetInvalidCountries()
{
yield return new object[] { "", "Country must not be empty" };
yield return new object[] { " ", "Country must not be empty" };
yield return new object[] { "123", "Country must contain only letters" };
}
[Theory]
[ChannelData("UI", "API")]
[ChannelMemberData(nameof(GetInvalidCountries))]
public void CreateOrder_InvalidCountry_ShouldFail(
Channel channel,
string country,
string expectedError)
{
// Test implementation
}
Channel + Inline Data with Additional Channels (Also)
Reduce UI test count by running only representative data rows on slow channels:
[Theory]
[ChannelData("API")]
[ChannelInlineData("20.00", "5", "100.00", Also = new[] { "UI" })] // API + UI
[ChannelInlineData("10.00", "3", "30.00")] // API only
[ChannelInlineData("15.50", "4", "62.00")] // API only
[ChannelInlineData("99.99", "1", "99.99")] // API only
public void PlaceOrder_ShouldCalculateCorrectBasePrice(
Channel channel,
string unitPrice,
string quantity,
string basePrice)
{
// Test implementation
}
// Generates 5 tests: API×4 rows + UI×1 row (the one with Also)
// Without Also: would be 8 tests (2 channels × 4 rows)
The Also property accepts an array of additional channel names. When specified, that data row runs on the base channels plus the additional channels. When omitted, the row runs on base channels only. This is fully backwards compatible.
Test Isolation
Mark tests that require isolation from other tests:
[Fact]
[Isolated("Deletes all orders from database")]
public void ClearAllOrders_ShouldDeleteAllRecords()
{
// This test modifies shared state
}
Filter isolated tests:
# Run ONLY isolated tests
dotnet test --filter "Category=isolated"
# Run all EXCEPT isolated tests
dotnet test --filter "Category!=isolated"
Best Practices
- Channel Naming - Use consistent channel names across your test suite (e.g., "UI", "API", "CLI")
- Isolation - Always mark tests with side effects using
[Isolated] - Test Data - Use
ChannelClassDataorChannelMemberDatafor complex test data
Why Optivem.Testing?
- Reduce Duplication - Write test logic once, run across all channels
- Better Coverage - Cartesian products ensure comprehensive testing
- Clear Intent - Attributes make test requirements explicit
- Type Safety -
Channeltype prevents string typos - xUnit Compatible - Works seamlessly with existing xUnit tests
Requirements
- .NET 8.0 or higher
- xUnit 2.x
License
Links
Built by Optivem
| 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. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- xunit.abstractions (>= 2.0.3)
- xunit.core (>= 2.9.3)
- xunit.extensibility.core (>= 2.9.3)
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.9 | 97 | 6/18/2026 |
| 1.1.8 | 2,036 | 4/21/2026 |
| 1.1.7 | 135 | 4/20/2026 |
| 1.1.6 | 653 | 4/11/2026 |
| 1.1.5 | 195 | 4/8/2026 |
| 1.1.4 | 316 | 4/8/2026 |
| 1.1.3 | 98 | 4/8/2026 |
| 1.1.2 | 99 | 4/8/2026 |
| 1.1.1 | 99 | 4/6/2026 |
| 1.1.0 | 373 | 4/6/2026 |
| 1.0.6 | 117 | 2/10/2026 |
| 1.0.5 | 2,869 | 2/9/2026 |
| 1.0.4 | 281 | 2/6/2026 |
| 1.0.3 | 113 | 2/6/2026 |
| 1.0.2 | 115 | 2/6/2026 |
| 1.0.1 | 112 | 2/6/2026 |
| 1.0.0 | 110 | 2/6/2026 |