TimeWarp.Jaribu 1.0.0-beta.8

Prefix Reserved
This is a prerelease version of TimeWarp.Jaribu.
dotnet add package TimeWarp.Jaribu --version 1.0.0-beta.8
                    
NuGet\Install-Package TimeWarp.Jaribu -Version 1.0.0-beta.8
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="TimeWarp.Jaribu" Version="1.0.0-beta.8" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="TimeWarp.Jaribu" Version="1.0.0-beta.8" />
                    
Directory.Packages.props
<PackageReference Include="TimeWarp.Jaribu" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add TimeWarp.Jaribu --version 1.0.0-beta.8
                    
#r "nuget: TimeWarp.Jaribu, 1.0.0-beta.8"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package TimeWarp.Jaribu@1.0.0-beta.8
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=TimeWarp.Jaribu&version=1.0.0-beta.8&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=TimeWarp.Jaribu&version=1.0.0-beta.8&prerelease
                    
Install as a Cake Tool

TimeWarp.Jaribu

Lightweight testing helpers for single-file C# programs and scripts.

Jaribu (Swahili: test/trial) provides a convention-based TestRunner pattern and assertion helpers for executable .cs files. It enables easy testing in single-file scenarios without heavy test frameworks.

Features

  • Convention over Configuration: Discover public static async Task methods as tests via reflection.
  • Assertion Helpers: Simple, fluent assertions inspired by Shouldly.
  • Attributes: Support for [Skip], [TestTag], [Timeout], [Input], and [ClearRunfileCache].
  • Parameterized Tests: Easy data-driven testing.
  • Tag Filtering: Run specific test groups.
  • Cache Management: Clear runfile cache for consistent testing.
  • Minimal Dependencies: Only Shouldly for assertions.

Installation

Add the NuGet package:

dotnet add package TimeWarp.Jaribu

Usage

Basic Test File

Create a single-file test script (e.g., my-tests.cs):

using static TimeWarp.Jaribu.TestHelpers;

public static class MyTests
{
    public static async Task BasicTest()
    {
        1.ShouldBe(1);
    }

    [TestTag("integration")]
    public static async Task IntegrationTest()
    {
        // Test logic here
    }
}

Run with:

dotnet run --project my-tests.cs

TestRunner

For programmatic use:

using TimeWarp.Jaribu;

// Simple usage - returns exit code (0 = success, 1 = failure)
int exitCode = await TestRunner.RunTests<MyTests>();

// With structured results - get detailed test information
TestRunSummary summary = await TestRunner.RunTestsWithResults<MyTests>();

// Access detailed results
Console.WriteLine($"Passed: {summary.PassedCount}");
Console.WriteLine($"Failed: {summary.FailedCount}");
Console.WriteLine($"Skipped: {summary.SkippedCount}");
Console.WriteLine($"Duration: {summary.TotalDuration}");

// Iterate over individual test results
foreach (TestResult result in summary.Results)
{
    Console.WriteLine($"{result.TestName}: {result.Outcome} ({result.Duration.TotalMilliseconds}ms)");
    if (result.FailureMessage is not null)
    {
        Console.WriteLine($"  Error: {result.FailureMessage}");
    }
}

Multi-Class Test Registration

Run tests from multiple test classes with aggregated results:

using TimeWarp.Jaribu;

// Register test classes explicitly (no assembly scanning)
TestRunner.RegisterTests<LexerTests>();
TestRunner.RegisterTests<ParserTests>();
TestRunner.RegisterTests<RoutingTests>();

// Run all registered and get exit code (0 = success, 1 = failure)
return await TestRunner.RunAllTests();

// Or with tag filter
return await TestRunner.RunAllTests(filterTag: "Unit");

// Or get full results with TestSuiteSummary
TestSuiteSummary summary = await TestRunner.RunAllTestsWithResults();
Console.WriteLine($"Total: {summary.TotalTests}, Passed: {summary.PassedCount}, Failed: {summary.FailedCount}");

// Access individual class results
foreach (TestRunSummary classResult in summary.ClassResults)
{
    Console.WriteLine($"{classResult.ClassName}: {classResult.PassedCount}/{classResult.TotalTests} passed");
}

Note: Use TestRunner.ClearRegisteredTests() to clear all registrations if needed.

Multi-File Test Orchestration

Organize tests across multiple files that work both standalone and aggregated:

  • Standalone mode: Run individual test files directly with dotnet file.cs
  • Multi mode: An orchestrator compiles multiple test files together with aggregated results

This pattern uses [ModuleInitializer] for auto-registration and conditional compilation to prevent double-execution.

Test file pattern
#!/usr/bin/dotnet --
#:project ../../Source/MyProject/MyProject.csproj

#if !JARIBU_MULTI
return await RunAllTests();
#endif

[TestTag("Unit")]
public class MyTests
{
    [ModuleInitializer]
    internal static void Register() => RegisterTests<MyTests>();

    public static async Task SomeTest()
    {
        // Test logic
    }
}

Key elements:

  • #!/usr/bin/dotnet -- enables direct execution as a script
  • #:project references dependencies (Jaribu, your project, etc.)
  • #if !JARIBU_MULTI only self-executes when run standalone
  • [ModuleInitializer] auto-registers when compiled in multi mode
Create an orchestrator

Create a simple entry point that runs all auto-registered tests:

#!/usr/bin/dotnet --
#:project ../Source/MyProject/MyProject.csproj

// Tests auto-registered via [ModuleInitializer]
return await RunAllTests();
Configure Directory.Build.props

Configure which test files to include and define the JARIBU_MULTI constant:

<Project>
  <PropertyGroup>
    <DefineConstants>$(DefineConstants);JARIBU_MULTI</DefineConstants>
  </PropertyGroup>
  <ItemGroup>
    <Compile Include="../my-tests-1.cs" />
    <Compile Include="../my-tests-2.cs" />
  </ItemGroup>
</Project>

This allows CI pipelines to run different subsets of tests by configuring separate orchestrators with different file includes.

Real-world example

Jaribu uses this pattern for its own test suite:

  • Tests/TimeWarp.Jaribu.Tests/jaribu-*.cs - Test files following the dual-mode pattern
  • Tests/TimeWarp.Jaribu.Tests/ci-tests/ - CI orchestrator with curated test selection

Structured Results Types

// Test outcome for each test
public enum TestOutcome { Passed, Failed, Skipped }

// Individual test result
public record TestResult(
    string TestName,
    TestOutcome Outcome,
    TimeSpan Duration,
    string? FailureMessage,
    string? StackTrace,
    IReadOnlyList<object?>? Parameters  // For parameterized tests
);

// Summary of entire test run
public record TestRunSummary(
    string ClassName,
    DateTimeOffset StartTime,
    TimeSpan TotalDuration,
    int PassedCount,
    int FailedCount,
    int SkippedCount,
    IReadOnlyList<TestResult> Results
)
{
    public int TotalTests => PassedCount + FailedCount + SkippedCount;
    public bool Success => FailedCount == 0;
}

// Summary of multiple test class runs
public record TestSuiteSummary(
    DateTimeOffset StartTime,
    TimeSpan TotalDuration,
    int TotalTests,
    int PassedCount,
    int FailedCount,
    int SkippedCount,
    IReadOnlyList<TestRunSummary> ClassResults
)
{
    public bool Success => FailedCount == 0;
}

Setup and CleanUp

Define Setup() and CleanUp() methods to run code before and after each test:

public static class MyTests
{
    public static async Task Setup()
    {
        // Runs before EACH test
        // Initialize test data, create temp files, etc.
        await Task.CompletedTask;
    }

    public static async Task CleanUp()
    {
        // Runs after EACH test
        // Clean up resources, delete temp files, etc.
        await Task.CompletedTask;
    }

    public static async Task Test1()
    {
        // Setup runs before this test
        // Test logic here
        // CleanUp runs after this test
    }

    public static async Task Test2()
    {
        // Setup runs before this test (fresh state)
        // Test logic here
        // CleanUp runs after this test
    }
}

Note: For one-time initialization, use static constructors or static field initialization:

public static class MyTests
{
    private static readonly ExpensiveResource Resource = InitializeResource();

    private static ExpensiveResource InitializeResource()
    {
        // One-time initialization
        return new ExpensiveResource();
    }
}

Documentation

See the developer documentation for advanced usage, attributes, and best practices.

Building from Source

  1. Clone the repository.
  2. Run dotnet build.
  3. Run tests with dotnet run --project Tests/TimeWarp.Jaribu.Tests/TimeWarp.Jaribu.Tests.csproj.

Contributing

Contributions welcome! See CONTRIBUTING.md for guidelines.

License

MIT License

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories (1)

Showing the top 1 popular GitHub repositories that depend on TimeWarp.Jaribu:

Repository Stars
TimeWarpEngineering/timewarp-nuru
Route-based CLI framework for .NET - Nuru means 'light' in Swahili
Version Downloads Last Updated
1.0.0-beta.8 137 12/23/2025
1.0.0-beta.7 140 12/22/2025
1.0.0-beta.6 238 12/16/2025
1.0.0-beta.5 442 11/20/2025
1.0.0-beta.4 126 11/15/2025
1.0.0-beta.3 135 10/8/2025
1.0.0-beta.2 222 10/8/2025