Table of Contents

FUnit - Unit Testing Framework for File-based Apps

FUnit FUnit.Run FUnit.Directives build ย  DeepWiki

๐Ÿ”ฌ Unit Testing Framework for File-based Apps

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

#:project ../src
#:package FUnit@*

return FUnit.Run(args, describe =>
{
    // Organize tests by test subject
    describe("Test Subject", it =>
    {
        it("should be ...", async () =>  // async can be used
        {
            // Write actual test here: Arrange, Act, Assert
        });

        it("can have multiple test cases", ...);
    });

    describe("Inspired by vitest", ...);
});
# Run unit test
dotnet run ./tests/my-test.cs

# OR use 'dnx' to run all "*test*.cs" (case-insensitive) in project folder
dnx FUnit.Run

๐Ÿ—ƒ๏ธ 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.
#:project ../src
#:package FUnit@*
#:package YourFavoriteAssertionLibrary@3.1.0

return FUnit.Run(args, describe =>
{
    // Organize tests by test subject
    describe("Test Subject", it =>
    {
        // Expected result and test function
        it("should be ...", () =>
        {
            // Write actual test here: Arrange, Act, Assert
        });

        // Other test cases
        it("should not ...", async () =>
        {
            // Async test can also be written
        });

        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 comparisons
Must.BeEqual(expected, actual);
Must.BeSameReference(expected, actual);

// Collection assertion
Must.HaveSameSequence(expected, actual);
Must.HaveSameUnorderedElements(expected, actual);

// Text assertion
Must.ContainText(text, substring);
Must.NotContainText(text, substring);

// Exception assertion
Must.Throw<T>("Expected error message", () => Test());

// Conditional assertion
Must.BeTrue(x is >= 0 and < 10);

// โŒ Avoid asserting high-level conditions on collections
// ex Instead of checking if a value is absent, assert the full expected content
Must.BeTrue(!list.Contains(x));  // โœ… Prefer: Must.HaveSameSequence(expectedList, actualList)

๐Ÿงช 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 FUnit.Run -c Release

# โœ… Correct (with '--')
dnx FUnit.Run -- -c Release

# โœ… Shortcut: without '-c' is valid for Debug or Release
dnx FUnit.Run Release

โš™ Advanced Options

Important

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

--concurrency int (Experimental)

# run max 10 tests simultaneously subject by subject
dotnet run ./tests/my-test.cs --concurrency 10

๐Ÿงพ 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@*

await GlobalSetupAsync();  // โœ… setup before Run call

int numFailures = 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 call

return numFailures;