Docs/Tools

Testing

Write and run tests.

On this page

Tests are test blocks next to your code. A build with --test runs them instead of main; normal builds leave them out. For other build options see Command line.

Write a test#

Valk
fn line_total(price: int, quantity: int) int {
    return price * quantity
}

test "Line total uses the requested quantity" {
    assert(line_total(12, 3) == 36)
    assert(line_total(12, 0) == 0)
}

assert records a check. Every executed assert counts in the summary, so in a long loop collect the result in a variable and assert once after it.

Run tests#

Terminal
valk build src --test --run
valk build src tests --test --run               # tests in their own directory
valk build src tests --test --filter "Line total" --run
valk build src tests --test -o test-bin         # build once
./test-bin --filter "Line total"                # pick tests without rebuilding

--filter keeps only tests whose name contains the text. A failed comparison shows both sides and where it is:

Text
#1 [code] assert(user.name == "Ada")
#1 [left] "ada"
#1 [right] "Ada"
#1 [file] tests/users.valk:12

The test program exits with a non-zero status when a test fails, so CI can run it directly.

Isolate tests that change shared state#

Regular tests run at the same time. Use synctest for a test that changes globals, environment variables or other process-wide state:

Valk
global counter: int (0)

synctest "Reset shared test state" {
    counter = 0
    counter++
    assert(counter == 1)
}

All regular tests finish before synctests start; synctests then run one at a time in declaration order. A test can still start its own coroutines to check concurrent code.

Test errors#

Valk
error ParseError (empty)

fn parse_name(text: String) String !ParseError {
    if text == "" : throw .empty
    return text
}

test "Empty names are rejected" {
    let failed = parse_name("") ! {
        assert(E.code == E.empty)
        return
    }
    assert(false)
}

Test helpers and platforms#

Valk
#if TEST
fn sample_users() Array[String] {
    return Array[String]{ "ada", "bob" }
}
#end

test "Paths use the platform separator" {
    #if OS == "win"
    assert("a\\b".contains("\\"))
    #else
    assert("a/b".contains("/"))
    #end
    assert(sample_users().length == 2)
}

TEST is true in test builds, so helpers inside #if TEST are not part of the release. OS and ARCH select code per platform; see Compile-time code. To check that code builds for another platform, lint for that target as shown in Cross compiling.