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