Testing

Asserts

  1. fn foo(mut v []int) {
  2. v[0] = 1
  3. }
  4. mut v := [20]
  5. foo(mut v)
  6. assert v[0] < 4

An assert statement checks that its expression evaluates to true. If an assert fails, the program will abort. Asserts should only be used to detect programming errors. When an assert fails it is reported to stderr, and the values on each side of a comparison operator (such as <, ==) will be printed when possible. This is useful to easily find an unexpected value. Assert statements can be used in any function.

Test files

  1. // hello.v
  2. module main
  3. fn hello() string {
  4. return 'Hello world'
  5. }
  6. fn main() {
  7. println(hello())
  8. }
  1. // failcompile
  2. // hello_test.v
  3. module main
  4. fn test_hello() {
  5. assert hello() == 'Hello world'
  6. }

To run the test above, use v hello_test.v. This will check that the function hello is producing the correct output. V executes all test functions in the file.

  • All test functions have to be inside a test file whose name ends in _test.v.
  • Test function names must begin with test_ to mark them for execution.
  • Normal functions can also be defined in test files, and should be called manually. Other symbols can also be defined in test files e.g. types.
  • There are two kinds of tests: external and internal.
  • Internal tests must declare their module, just like all other .v files from the same module. Internal tests can even call private functions in the same module.
  • External tests must import the modules which they test. They do not have access to the private functions/types of the modules. They can test only the external/public API that a module provides.

In the example above, test_hello is an internal test, that can call the private function hello() because hello_test.v has module main, just like hello.v, i.e. both are part of the same module. Note also that since module main is a regular module like the others, internal tests can be used to test private functions in your main program .v files too.

You can also define these special test functions in a test file:

  • testsuite_begin which will be run before all other test functions.
  • testsuite_end which will be run after all other test functions.

If a test function has an error return type, any propagated errors will fail the test:

  1. import strconv
  2. fn test_atoi() ? {
  3. assert strconv.atoi('1')? == 1
  4. assert strconv.atoi('one')? == 1 // test will fail
  5. }

Running tests

To run test functions in an individual test file, use v foo_test.v.

To test an entire module, use v test mymodule. You can also use v test . to test everything inside your current folder (and subfolders). You can pass the -stats option to see more details about the individual tests run.

You can put additional test data, including .v source files in a folder, named testdata, right next to your _test.v files. V’s test framework will ignore such folders, while scanning for tests to run. This is useful, if you want to put .v files with invalid V source code, or other tests, including known failing ones, that should be run in a specific way/options by a parent _test.v file.

NB: the path to the V compiler, is available through @VEXE, so a _test.v file, can easily run other test files like this:

  1. // oksyntax
  2. import os
  3. fn test_subtest() {
  4. res := os.execute('${os.quoted_path(@VEXE)} other_test.v')
  5. assert res.exit_code == 1
  6. assert res.output.contains('other_test.v does not exist')
  7. }