• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1<a id="top"></a>
2# Data Generators
3
4Data generators (also known as _data driven/parametrized test cases_)
5let you reuse the same set of assertions across different input values.
6In Catch2, this means that they respect the ordering and nesting
7of the `TEST_CASE` and `SECTION` macros, and their nested sections
8are run once per each value in a generator.
9
10This is best explained with an example:
11```cpp
12TEST_CASE("Generators") {
13    auto i = GENERATE(1, 2, 3);
14    SECTION("one") {
15        auto j = GENERATE( -3, -2, -1 );
16        REQUIRE(j < i);
17    }
18}
19```
20
21The assertion in this test case will be run 9 times, because there
22are 3 possible values for `i` (1, 2, and 3) and there are 3 possible
23values for `j` (-3, -2, and -1).
24
25
26There are 2 parts to generators in Catch2, the `GENERATE` macro together
27with the already provided generators, and the `IGenerator<T>` interface
28that allows users to implement their own generators.
29
30## Provided generators
31
32Catch2's provided generator functionality consists of three parts,
33
34* `GENERATE` macro,  that serves to integrate generator expression with
35a test case,
36* 2 fundamental generators
37  * `ValueGenerator<T>` -- contains only single element
38  * `ValuesGenerator<T>` -- contains multiple elements
39* 5 generic generators that modify other generators
40  * `FilterGenerator<T, Predicate>` -- filters out elements from a generator
41  for which the predicate returns "false"
42  * `TakeGenerator<T>` -- takes first `n` elements from a generator
43  * `RepeatGenerator<T>` -- repeats output from a generator `n` times
44  * `MapGenerator<T, U, Func>` -- returns the result of applying `Func`
45  on elements from a different generator
46  * `ChunkGenerator<T>` -- returns chunks (inside `std::vector`) of n elements from a generator
47* 3 specific purpose generators
48  * `RandomIntegerGenerator<Integral>` -- generates random Integrals from range
49  * `RandomFloatGenerator<Float>` -- generates random Floats from range
50  * `RangeGenerator<T>` -- generates all values inside a specific range
51
52The generators also have associated helper functions that infer their
53type, making their usage much nicer. These are
54
55* `value(T&&)` for `ValueGenerator<T>`
56* `values(std::initializer_list<T>)` for `ValuesGenerator<T>`
57* `filter(predicate, GeneratorWrapper<T>&&)` for `FilterGenerator<T, Predicate>`
58* `take(count, GeneratorWrapper<T>&&)` for `TakeGenerator<T>`
59* `repeat(repeats, GeneratorWrapper<T>&&)` for `RepeatGenerator<T>`
60* `map(func, GeneratorWrapper<T>&&)` for `MapGenerator<T, T, Func>` (map `T` to `T`)
61* `map<T>(func, GeneratorWrapper<U>&&)` for `MapGenerator<T, U, Func>` (map `U` to `T`)
62* `chunk(chunk-size, GeneratorWrapper<T>&&)` for `ChunkGenerator<T>`
63* `random(IntegerOrFloat a, IntegerOrFloat b)` for `RandomIntegerGenerator` or `RandomFloatGenerator`
64* `range(start, end)` for `RangeGenerator<T>` with a step size of `1`
65* `range(start, end, step)` for `RangeGenerator<T>` with a custom step size
66
67
68And can be used as shown in the example below to create a generator
69that returns 100 odd random number:
70
71```cpp
72TEST_CASE("Generating random ints", "[example][generator]") {
73    SECTION("Deducing functions") {
74        auto i = GENERATE(take(100, filter([](int i) { return i % 2 == 1; }, random(-100, 100))));
75        REQUIRE(i > -100);
76        REQUIRE(i < 100);
77        REQUIRE(i % 2 == 1);
78    }
79}
80```
81
82
83Apart from registering generators with Catch2, the `GENERATE` macro has
84one more purpose, and that is to provide simple way of generating trivial
85generators, as seen in the first example on this page, where we used it
86as `auto i = GENERATE(1, 2, 3);`. This usage converted each of the three
87literals into a single `ValueGenerator<int>` and then placed them all in
88a special generator that concatenates other generators. It can also be
89used with other generators as arguments, such as `auto i = GENERATE(0, 2,
90take(100, random(300, 3000)));`. This is useful e.g. if you know that
91specific inputs are problematic and want to test them separately/first.
92
93**For safety reasons, you cannot use variables inside the `GENERATE` macro.**
94
95You can also override the inferred type by using `as<type>` as the first
96argument to the macro. This can be useful when dealing with string literals,
97if you want them to come out as `std::string`:
98
99```cpp
100TEST_CASE("type conversion", "[generators]") {
101    auto str = GENERATE(as<std::string>{}, "a", "bb", "ccc");`
102    REQUIRE(str.size() > 0);
103}
104```
105
106## Generator interface
107
108You can also implement your own generators, by deriving from the
109`IGenerator<T>` interface:
110
111```cpp
112template<typename T>
113struct IGenerator : GeneratorUntypedBase {
114    // via GeneratorUntypedBase:
115    // Attempts to move the generator to the next element.
116    // Returns true if successful (and thus has another element that can be read)
117    virtual bool next() = 0;
118
119    // Precondition:
120    // The generator is either freshly constructed or the last call to next() returned true
121    virtual T const& get() const = 0;
122};
123```
124
125However, to be able to use your custom generator inside `GENERATE`, it
126will need to be wrapped inside a `GeneratorWrapper<T>`.
127`GeneratorWrapper<T>` is a value wrapper around a
128`std::unique_ptr<IGenerator<T>>`.
129
130For full example of implementing your own generator, look into Catch2's
131examples, specifically
132[Generators: Create your own generator](../examples/300-Gen-OwnGenerator.cpp).
133
134