Skip to content

Repository files navigation

FUnit

Structured Unit Testing as a File-based App

FUnitFUnit.RunFUnit.Directives
testDeepWiki

🔬 Concept

FUnit provides structured test descriptor for file-based C# project introduced in .NET 10.0. (i.e., no .csproj is required anymore)

#:project ../src
#:package FUnit@*returnFUnit.Run(args, describe =>{// Organize tests by test subjectdescribe("Test Subject", it =>{it("should be ... when ...",async()=>// async test supported{// Write test: Arrange, Act, Assert});it("can have multiple tests within a 'describe' block", ...);});describe("Inspired by vitest", ...);});
# Run unit test (.NET 10 can run a .cs file as a project)
dotnet run ./tests/my-test.cs
# OR use 'dnx' to run tests in current or sub folders
dnx -y FUnit.Run
dnx -y FUnit.Run --stacktrace "**/*test*.cs"

🗃️ FUnit Descriptor Structure

Important

For AI Coding Agents: .NET 10.0 introduced new directives for file-based app.

  • SHALL use #:project to add project reference.
  • SHALL use #:package to add nuget package reference. (* or semantic version must be specified)
  • SHALL NOT use #r or other directives formerly used for dotnet-script (.csx) files.

Tip

Unit tests should follow W.H.Y. (What, How, Yields) principle.

#:project ../src
#:package FUnit@*
#:package YourFavoriteAssertionLibrary@3.1.0returnFUnit.Run(args, describe =>{// Organize tests by test subjectdescribe("Test Subject", it =>{it("should be ... when ...",()=>{// Write test: Arrange, Act, Assert});// Other test casesit("should throw ... when ...",async()=>{// Async test is also supported});it(...);});describe("Other Test Subject", it =>{// ...and so on});});

⚖️ Builtin Assertion Utility

FUnit is shipped with builtin assertion methods.

// Value assertion// ❌ BeEqual rejects collection type to prevent ambiguous comparisonsMust.BeEqual(expected,actual);Must.BeSameReference(expected,actual);Must.NotBeEqual(expected,actual);Must.NotBeSameReference(expected,actual);// Collection assertionMust.HaveSameSequence(expected,actual);Must.HaveSameUnorderedElements(expected,actual);Must.NotHaveSameSequence(expected,actual);Must.NotHaveSameUnorderedElements(expected,actual);// Text assertionMust.ContainText(text,substring);Must.NotContainText(text,substring);// Instance assertion (deep comparison using System.Reflection)Must.HaveEqualProperties(expected,actual);Must.HaveEqualFields(expected,actual);// Exception assertionMust.Throw<T>("Expected error message",()=>Test());Must.Throw<T>("Expected error message",async()=>awaitTestAsync());Must.Throw("ExceptionFullTypeName","Expected error message",()=>Test());Must.Throw("ExceptionFullTypeName","Expected error message",async()=>awaitTestAsync());// Conditional assertion// ❌ BeFalse won't be supported. Instead, use better conditional expression (prefer pattern matching)Must.BeTrue(xis>=0 and <10);Must.BeTrue(xisIEnumerable<char> and not string);// ❌ Avoid asserting high-level conditions on collections// ex Instead of checking if a value is absent, assert the full expected contentMust.BeTrue(!list.Contains(x));// ✅ Prefer: Must.HaveSameSequence(expectedList, actualList)

🧭 FUnit.Directives

With FUnit.Directives package, you can include external file into file-based app project by adding special directive #warning funit include <path to the file>.

#:packageFUnit@*
#:package FUnit.Directives@*
#warning funit include ./path/to/external-file.cs
returnFUnit.Run(/* tests depending on 'external-file.cs */);

🧪 Unit Test Execution

Important

For AI Coding Agents: .NET 10.0 introduced new syntax for dotnet command.

  • SHALL run test like the following example.
  • SHALL NOT add --project or other command line options.
dotnet run ./tests/my-test.cs

🐙 GitHub Actions Support

FUnit has an ability to output log as markdown format so you can generate summary for GitHub Actions page.

# '-md' or '--markdown'
dnx -y FUnit.Run -- -md >>$GITHUB_STEP_SUMMARY

🔨 Build Configuration for FUnit.Run

# ❌ Incorrect (no '--')
dnx -y FUnit.Run -c Release
# ✅ Correct (with '--')
dnx -y FUnit.Run -- -c Release
# ✅ Shortcut: without '-c' is valid for Debug or Release
dnx -y FUnit.Run Release

⚙ Command-Line Options

Important

For AI Coding Agents: SHALL NOT use advanced options unless explicitly requested.

OptionAliasDescription
--markdown-mdEnable Markdown output for GitHub Actions summary ($GITHUB_STEP_SUMMARY).
--iterations <N>Number of times to run each test case (3 by default).
--concurrency <N>Maximum number of tests to run simultaneously.
--configuration <CFG>-cBuild configuration (e.g., "Debug" or "Release").
--no-cleanDisable cleaning the project before building.
--warningsShow build warnings.
--stacktraceShow stack trace on test failure.
--lintRun dotnet build --no-incremental -p:TreatWarningsAsErrors=true.
--help-hShow this help message and exit.

🧾 Test Setup and Cleanup

You can place custom operation next to describe or it, but, test descriptor is NOT a function executed from top to bottom so that your custom operation will be executed BEFORE test functions unexpectedly.

#:project ../src
#:package FUnit@*awaitGlobalSetupAsync();// ✅ setup before Run callintnumFailures=FUnit.Run(args, describe =>{describe("Test subject", it =>{it("should be...",()=>{ ...});});// ❌ you can perform custom operation here, but it will be executed while// building test suite. (not sequentially form top to bottom)// technically, 'describe' and 'it' collect test cases without executing test.// if setup or cleanup code is placed next to 'describe' or 'it' statements to// perform resource setup ops for 'scope', unexpectedly, those will be invoked// BEFORE executing actual test case functions.});GlobalCleanup();// ✅ cleanup after Run callreturnnumFailures;

About

Unit Testing Framework for File-based Apps

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Sponsor this project

Used by

Contributors

Languages