# How to Write and Run Unit Tests with `forc test` in Sway

> Learn to write and run unit tests in Sway using forc test. Discover how this command compiles and executes your tests on the Fuel VM, providing detailed results.

- Repository: [Fuel Labs/sway](https://github.com/FuelLabs/sway)
- Tags: how-to-guide
- Published: 2026-03-05

---

**The `forc test` command compiles and executes all functions annotated with `#[test]` in a Sway package, running them on the Fuel VM and reporting pass/fail status, gas usage, and optional logs.**

The `forc test` command is the official test harness for the Sway language, maintained in the FuelLabs/sway repository. It provides a zero-configuration workflow for unit testing smart contracts and scripts by compiling each `#[test]` function as an independent entry point and executing it within the Fuel VM environment.

## Writing Tests for `forc test`

### Basic Unit Tests

Mark any function with the `#[test]` attribute to register it as a test entry point. The function must take no arguments and return unit `()`.

```sway
#[test]
fn test_meaning_of_life() {
    assert(6 * 7 == 42);
}

```

### Testing Revert Conditions

Use `#[test(should_revert)]` when you expect a test to fail by reverting. The test passes only if execution triggers a `rvrt` instruction.

```sway
#[test(should_revert)]
fn test_fails_on_wrong_result() {
    // This will revert because the assertion is false.
    assert_eq(6 * 6, 42);
}

```

To assert a specific revert code, provide the 64-bit decimal value as a string:

```sway
#[test(should_revert = "18446744073709486084")]
fn test_specific_revert() {
    assert_eq(6 * 6, 42);
}

```

### Testing Contract Calls

When testing contract logic, `forc test` automatically builds the contract without tests first to compute `CONTRACT_ID`, then rebuilds with tests and injects that identifier into the test namespace.

```sway
contract;

abi MyContract {
    fn greet() -> bool;
}

impl MyContract for Contract {
    fn greet() -> bool {
        true
    }
}

#[test]
fn test_contract_call() {
    let caller = abi(MyContract, CONTRACT_ID);
    let result = caller.greet {}();
    assert(result == true);
}

```

### Logging Inside Tests

Use the `log()` function to emit values during test execution. Logs are captured as `Log` or `LogData` receipts on the Fuel VM.

```sway
script;

fn main() {}

#[test]
fn test_with_logs() {
    let a = 10;
    log(a);
    let b = 30;
    log(b);
    assert_eq(a, 10);
    assert_eq(b, 30);
}

```

## Running Tests with `forc test`

### Basic Invocation

Execute all tests in the current package:

```bash
forc test

```

This compiles the package with the `is_test` flag set in [`sway-core/src/build_config.rs`](https://github.com/FuelLabs/sway/blob/main/sway-core/src/build_config.rs) and runs each test binary on the Fuel VM.

### Filtering Tests

Run only tests whose names contain a specific substring:

```bash
forc test mean

```

Run a single test by exact name match:

```bash
forc test --filter-exact test_meaning_of_life

```

The filtering logic is implemented via `TestFilter` in [`forc/src/cli/commands/test.rs`](https://github.com/FuelLabs/sway/blob/main/forc/src/cli/commands/test.rs).

### Controlling Parallelism

By default, `forc test` uses as many threads as available CPU cores. Force single-threaded execution for deterministic log ordering:

```bash
forc test --test-threads 1

```

This maps to `TestRunnerCount` in the `forc_test` crate.

### Output and Debugging Options

Control the verbosity and format of test results:

- `-l` / `--logs` — Decode and print `Log`/`LogData` receipts (see `print_test_output` in [`forc/src/cli/commands/test.rs`](https://github.com/FuelLabs/sway/blob/main/forc/src/cli/commands/test.rs)).
- `--raw-logs` — Display raw log receipts as JSON.
- `--pretty` — Pretty-print raw JSON logs.
- `--reverts` — Show revert codes and error messages for failing or `should_revert` tests.
- `--dbgs` — Print debug output from `dbg!` captures.
- `--silent` — Suppress the summary output (useful in CI pipelines).

Example combining multiple flags:

```bash
forc test --logs --raw-logs --pretty --reverts

```

### Gas Usage Reporting

`forc test` reports gas consumed for each test (e.g., `(..., 512 gas)`). Override the default gas cost table with:

```bash
forc test --gas-costs testnet

```

Valid options include `built-in`, `mainnet`, `testnet`, or a path to a custom JSON file.

## How `forc test` Works Internally

The test harness follows a multi-stage pipeline defined in the `forc-test` crate and orchestrated from [`forc/src/cli/commands/test.rs`](https://github.com/FuelLabs/sway/blob/main/forc/src/cli/commands/test.rs):

1. **CLI Parsing** — The `Command` struct in [`test.rs`](https://github.com/FuelLabs/sway/blob/main/test.rs) defines all flags (filtering, threading, output options) and the `exec` entry point.
2. **Build Step** — `forc_test::build(opts)` compiles each `#[test]` function as an independent entry point. The `is_test` flag in [`sway-core/src/build_config.rs`](https://github.com/FuelLabs/sway/blob/main/sway-core/src/build_config.rs) enables test-specific compilation paths.
3. **Execution** — `built_tests.run(...)` spawns the Fuel VM for each compiled artifact, respecting `TestRunnerCount` for parallelism.
4. **Result Processing** — `print_tested_pkg` iterates over `TestResult` structs, formatting pass/fail markers, gas usage, and delegating to `print_test_output` for logs.
5. **Exit Code** — If any test fails, `exec` returns exit code **101**; otherwise **0** (lines 57–65 of [`test.rs`](https://github.com/FuelLabs/sway/blob/main/test.rs)).

## Quick Reference Cheat Sheet

```bash

# Run all tests

forc test

# Run tests matching a substring

forc test math

# Run exact test name

forc test --filter-exact test_math_addition

# Single-threaded execution

forc test --test-threads 1

# Show logs and revert details

forc test --logs --reverts

# Pretty-print raw logs

forc test --raw-logs --pretty

# Custom gas costs

forc test --gas-costs testnet

# Silent mode for CI

forc test --silent

```

## Summary

- Annotate test functions with `#[test]` or `#[test(should_revert)]` to mark them as entry points for `forc test`.
- The command compiles each test as an independent binary, executes it on the Fuel VM, and reports gas usage alongside pass/fail status.
- Use `--filter-exact` to run single tests and `--test-threads` to control parallelism.
- Access logs with `--logs` or `--raw-logs`, and inspect revert codes with `--reverts`.
- The implementation resides in [`forc/src/cli/commands/test.rs`](https://github.com/FuelLabs/sway/blob/main/forc/src/cli/commands/test.rs) and the `forc-test` crate, using the `is_test` flag in [`sway-core/src/build_config.rs`](https://github.com/FuelLabs/sway/blob/main/sway-core/src/build_config.rs) to enable test-specific compilation.

## Frequently Asked Questions

### How do I run a single specific test with `forc test`?

Use the `--filter-exact` flag followed by the full function name. For example, `forc test --filter-exact test_meaning_of_life` runs only that function. Without this flag, providing a positional argument acts as a substring filter, running all tests whose names contain the provided text.

### Can I view logs from tests that pass?

By default, logs are suppressed for passing tests. To view them, add the `-l` or `--logs` flag to decode and display `Log` and `LogData` receipts. For raw VM receipt data, use `--raw-logs`, and combine with `--pretty` for formatted JSON output.

### What exit code does `forc test` return on failure?

The command exits with status **101** if any test fails, and **0** on complete success. This behavior is defined in the `exec` function within [`forc/src/cli/commands/test.rs`](https://github.com/FuelLabs/sway/blob/main/forc/src/cli/commands/test.rs) (lines 57–65) and follows Rust’s testing convention, making it suitable for CI pipelines that check exit status.

### How do I test that a function reverts with a specific error code?

Annotate the test with `#[test(should_revert = "18446744073709486084")]`, providing the 64-bit decimal representation of the expected revert code. The test harness compares the actual revert code from the VM against this value, and the test passes only if they match.