7.2.261-stable

Testing with GreyCat


Testing is essential to ensure that a GreyCat project behaves as expected. This section provides guidance on how to set up and run tests effectively.

GreyCat natively integrates a framework for unit tests.

To run your tests simply execute the following command greycat test.

Test function naming convention

Function decorated by a @test pragma will be considered as a test unit by GreyCat.

@test
fn my_test_function() {
    Assert::isNull(null); // probably valid assertion...
}

Output from running greycat test

project::my_test_function ok (5us)

tests success: 1, failed: 0, skipped: 0

A module (file) can integrate several tests directly within the sources.

Execution order of all tests will follow definition order within one module.

//project.gcl
@test
fn test1() {}

@test
fn test2() {}

Output

project::test1 ok (1us)
project::test2 ok (0us)

tests success: 2, failed: 0, skipped: 0

All the `@test` in a module share the same context. Therefore, any modifications to a module variable in a test will be available to other in the same module

Note: the changes will not effectively be saved to disk, they will be discarded at the end of the module test execution

Setup / Teardown

If you are doing any kind of integration test between the database and business logic you would require a setup step to populate the database with data. The following tests would all be valid:

var n: node<int?>;

/// This will be run once prior to any test in the module
fn setup() {
    // do some setup..
    n.set(1);
}

/// This will be run once after the tests in the module
fn teardown() {
    // do some clean up..
}

@test
fn some_test() {
    Assert::equals(*n, 1);
    n.set(42);
}

@test
fn following_test() {
    Assert::equals(*n, 42);
}
project::setup ok (7us)
project::some_test ok (5us)
project::following_test ok (0us)
project::teardown ok (0us)

tests success: 4, failed: 0, skipped: 0

The setup/teardown behavior comes in handy for reading sample data and creating indexes or timeseries, or even spawning Docker containers.

Test modules naming convention

If a file ends with _test.gcl suffix It will be excluded from the build when running greycat build.

A classical project tree would look like this, you are of course free to implement your own structure.

├── project.gcl
├── src
│   ├── api.gcl
│   ├── model.gcl
│   └── model_test.gcl
└── test
    └── api_test.gcl

  • src directory contains model and api sources and eventually simple unit tests
  • test contains every remaining tests sources usually bigger integration tests, that you want to exclude from the final build

GreyCat test command return code

GreyCat test command return 0 in case of success (CI script friendly)

greycat test
echo $?
> 0

and non zero in case of failure

@test
fn fail(){ throw "very bad";}
project::fail failed (428us)
Error: very bad
    at fail (project.gcl:98:30)

tests success: 0, failed: 1
echo $?
> 5

Assert type

/// `Assert` is mainly used for testing purposes.
/// It verifies that assertions you make on the state of your data is correct, or throws an `Error`.
type Assert {
  /// Verifies that `a` is equal to `b`, throws an error if not. `a` and `b` can be of any type.
  static native fn equals(a: any?, b: any?);
  /// Verifies that `a` is equal to `b`, throws an error if not. `a` and `b` must be floats.
  static native fn equalsd(a: float, b: float, epsilon: float);
  /// Verifies that `a` is equal to `b`, throws an error if not. `a` and `b` must be tensors.
  static native fn equalst(a: Tensor, b: Tensor, epsilon: float);
  /// Verifies that `v` is true, throws an error if not.
  static native fn isTrue(v: bool);
  /// Verifies that `v` is false, throws an error if not.
  static native fn isFalse(v: bool);
  /// Verifies that `v` is null, throws an error if not.
  static native fn isNull(v: any?);
  /// Verifies that `v` is not null, throws an error if not.
  static native fn isNotNull(v: any?);
}