원클릭으로
jaribu
Write tests using the Jaribu framework for .NET 10 file-based apps (runfiles)
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Write tests using the Jaribu framework for .NET 10 file-based apps (runfiles)
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
| name | jaribu |
| description | Write tests using the Jaribu framework for .NET 10 file-based apps (runfiles) |
Jaribu is an attribute-based testing framework for .NET 10 runfiles. Tests are standalone executables.
Jaribu uses a sink-based architecture where all test output flows through ITestResultSink:
TestRunner.RunTestsAsync(sink)
├── TerminalSink → Console output (dotnet run single-file)
├── MtpSink → IMessageBus → VS/Rider/dotnet test
└── NullSink → Silent (testing/benchmarking)
| Type | Purpose |
|---|---|
TestNodeState | Enum: Discovered, InProgress, Passed, Failed, Skipped, Timeout, Error, Cancelled |
TestNodeInfo | Record: Uid, DisplayName, State, Duration?, Exception?, Message?, Parameters? |
TestRunStats | Record: ClassName, StartTime, Duration, PassedCount, FailedCount, SkippedCount |
ITestResultSink | Interface for receiving test lifecycle events |
#!/usr/bin/dotnet --
#if !JARIBU_MULTI
return await RunAllTests();
#endif
namespace Your.Namespace.Here
{
[TestTag("CategoryName")]
public class YourTestClassName
{
[ModuleInitializer]
internal static void Register() => RegisterTests<YourTestClassName>();
public static async Task Should_describe_expected_behavior()
{
// Arrange
// Act
// Assert
await Task.CompletedTask;
}
}
} // namespace Your.Namespace.Here
public static async TaskUse a hierarchical SUT_Action_Given_Should_Result pattern:
| Element | Location | Example |
|---|---|---|
| SUT (System Under Test) | Namespace | ShellBuilder_ |
| Action (method being tested) | Class (prefix) | CaptureAsync_Given_ |
| Given (precondition/scenario) | Method (prefix) | EchoCommand_ |
| Result (expected outcome) | Method (suffix) | Should_CaptureStdout |
Full qualified test name: ShellBuilder_.CaptureAsync_Given_.EchoCommand_Should_CaptureStdout
namespace ShellBuilder_
{
[TestTag("Core")]
public class CaptureAsync_Given_
{
[ModuleInitializer]
internal static void Register() => RegisterTests<CaptureAsync_Given_>();
public static async Task EchoCommand_Should_CaptureStdout()
{
CommandOutput output = await Shell.Builder("echo").WithArguments("Hello").CaptureAsync();
output.Stdout.Trim().ShouldBe("Hello");
}
public static async Task MultipleArgs_Should_PassAllArgs()
{
CommandOutput output = await Shell.Builder("echo").WithArguments("a", "b").CaptureAsync();
output.Stdout.Trim().ShouldBe("a b");
}
}
public class WithArguments_Given_
{
[ModuleInitializer]
internal static void Register() => RegisterTests<WithArguments_Given_>();
public static async Task EmptyArray_Should_ExecuteWithNoArgs()
{
// ...
}
}
}
Called automatically before/after EACH test:
public static async Task Setup()
{
// Called before EACH test
await Task.CompletedTask;
}
public static async Task CleanUp()
{
// Called after EACH test (even if test fails)
await Task.CompletedTask;
}
[Input("value1")]
[Input("value2")]
[Input("value3")]
public static async Task Should_handle_input(string input)
{
// Runs once per [Input] attribute
await Task.CompletedTask;
}
| Attribute | Purpose |
|---|---|
[TestTag("Name")] | Tag for filtering (class or method) |
[Skip("reason")] | Skip test with reason |
[Timeout(ms)] | Set timeout in milliseconds |
[Input("value")] | Data-driven test input |
[ModuleInitializer] | Register class for multi-mode |
[ClearRunfileCache] | Clear cache (standalone only, removed) |
result.ShouldBe(expected);
result.ShouldNotBe(unexpected);
result.ShouldBeNull();
result.ShouldNotBeNull();
flag.ShouldBeTrue();
flag.ShouldBeFalse();
count.ShouldBeGreaterThan(0);
text.ShouldContain("substring");
text.ShouldStartWith("prefix");
text.ShouldBeEmpty();
list.ShouldContain(item);
Should.Throw<Exception>(() => ThrowingMethod());
Format: {sut}.{action}.cs (kebab-case)
This aligns with the naming convention - one file per Action keeps files small and token-efficient.
Examples:
shell-builder.capture-async.cs # ShellBuilder_.CaptureAsync_Given_.*
shell-builder.to-command-string.cs # ShellBuilder_.ToCommandString_Given_.*
shell-builder.pipe.cs # ShellBuilder_.Pipe_Given_.*
shell-builder.run-async.cs # ShellBuilder_.RunAsync_Given_.*
Why one file per Action?
# Make executable and run (Runfile Mode)
chmod +x my-test.cs
./my-test.cs
# Or with dotnet
dotnet run my-test.cs
# Filter by tag
JARIBU_FILTER_TAG=Lexer ./my-test.cs
# M.T.P. Mode (IDE integration, dotnet test)
dotnet test
# List discovered tests
dotnet run -- --list-tests
// Simple usage - returns exit code (backward compatible)
int exitCode = await TestRunner.RunTests<MyTests>();
// Sink-based - get stats with console output
using TerminalSink sink = new();
TestRunStats stats = await TestRunner.RunTestsAsync<MyTests>(sink);
// Silent execution - for programmatic use
TestRunStats stats = await TestRunner.RunTestsAsync<MyTests>(NullSink.Instance);
// Non-generic - for reflection-based execution
TestRunStats stats = await TestRunner.RunTestsAsync(typeof(MyTests), sink);
<ItemGroup>
<PackageReference Include="TimeWarp.Jaribu" />
<PackageReference Include="Shouldly" />
</ItemGroup>
<ItemGroup>
<Using Include="Shouldly" />
<Using Include="System.Console" Static="true" />
<Using Include="TimeWarp.Jaribu" />
<Using Include="TimeWarp.Jaribu.TestRunner" Static="true" />
</ItemGroup>
/tmp directorytests/ directory in the projecttemp- (e.g., temp-test-which.cs)Tests exist to expose bugs. A failing test is doing its job.
NEVER:
When a test fails:
Valid reasons to skip:
DO:
Should_ namesDON'T: