Optivem.Testing
1.1.6
dotnet add package Optivem.Testing --version 1.1.6
NuGet\Install-Package Optivem.Testing -Version 1.1.6
<PackageReference Include="Optivem.Testing" Version="1.1.6" />
<PackageVersion Include="Optivem.Testing" Version="1.1.6" />
<PackageReference Include="Optivem.Testing" />
paket add Optivem.Testing --version 1.1.6
#r "nuget: Optivem.Testing, 1.1.6"
#:package Optivem.Testing@1.1.6
#addin nuget:?package=Optivem.Testing&version=1.1.6
#tool nuget:?package=Optivem.Testing&version=1.1.6
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, test isolation markers, and time-dependent test support.
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]) - Time-Dependent Tests - Mark time-sensitive tests (
[Time]) - .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"
Time-Dependent Tests
Mark tests that depend on specific times:
[Fact]
[Time("2024-01-15T17:30:00Z")]
public void DiscountRate_ShouldBe15Percent_WhenAfter5pm()
{
// Test implementation
}
Filter time-dependent tests:
# Run ONLY time-dependent tests
dotnet test --filter "Category=time"
# Run all EXCEPT time-dependent tests
dotnet test --filter "Category!=time"
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 - Time Tests -
[Time]tests are automatically marked as[Isolated]
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.