|
| 1 | +package truth // import "go.starlark.net/lib/truth" |
| 2 | + |
| 3 | +Package truth defines builtins and methods to express test |
| 4 | +assertions within Starlark programs in the fashion of https://truth.dev |
| 5 | + |
| 6 | +This package is a Starlark port of PyTruth (2c3717ddad 2021-03-10) |
| 7 | +https://github.com/google/pytruth |
| 8 | + |
| 9 | +The Starlark: |
| 10 | + |
| 11 | + assert.that(a).is_equal_to(b) |
| 12 | + assert.that(c).named("my value").is_true() |
| 13 | + assert.that(d).contains(a) |
| 14 | + assert.that(d).contains_all_of(a, b).in_order() |
| 15 | + assert.that(d).contains_any_of(a, b, c) |
| 16 | + |
| 17 | +is equivalent to the following Python: |
| 18 | + |
| 19 | + from truth.truth import AssertThat |
| 20 | + AssertThat(a).IsEqualTo(b) |
| 21 | + AssertThat(c).Named("my value").IsTrue() |
| 22 | + AssertThat(d).Contains(a) |
| 23 | + AssertThat(d).ContainsAllOf(a, b).InOrder() |
| 24 | + AssertThat(d).ContainsAnyOf(a, b, c) |
| 25 | + |
| 26 | +Often, tests assert a relationship between a value produced by the test (the |
| 27 | +"actual" value) and some reference value (the "expected" value). It is |
| 28 | +strongly recommended that the actual value is made the subject of the |
| 29 | +assertion. For example: |
| 30 | + |
| 31 | + assert.that(actual).is_equal_to(expected) # Recommended. |
| 32 | + assert.that(expected).is_equal_to(actual) # Not recommended. |
| 33 | + assert.that(actual).is_in(expected_possibilities) # Recommended. |
| 34 | + assert.that(expected_possibilities).contains(actual) # Not recommended. |
| 35 | + |
| 36 | +Some assertions |
| 37 | + |
| 38 | + assert.that(a).is_equal_to(b) |
| 39 | + assert.that(a).is_not_equal_to(b) |
| 40 | + assert.that(a).is_truthy() |
| 41 | + assert.that(a).is_falsy() |
| 42 | + assert.that(a).is_true() |
| 43 | + assert.that(a).is_false() |
| 44 | + assert.that(a).is_none() |
| 45 | + assert.that(a).is_not_none() |
| 46 | + assert.that(a).is_in(b) |
| 47 | + assert.that(a).is_any_of(b, c, d) |
| 48 | + assert.that(a).is_not_in(b) |
| 49 | + assert.that(a).is_none_of(b, c, d) |
| 50 | + assert.that(a).is_of_type(type(b)) |
| 51 | + assert.that(a).is_not_of_type(type(b)) |
| 52 | + assert.that(a).has_attribute(b) |
| 53 | + assert.that(a).does_not_have_attribute(b) |
| 54 | + assert.that(a).is_callable() |
| 55 | + assert.that(a).is_not_callable() |
| 56 | + assert.that(a).is_less_than(b) |
| 57 | + assert.that(a).is_greater_than(b) |
| 58 | + assert.that(a).is_at_most(b) |
| 59 | + assert.that(a).is_at_least(b) |
| 60 | + |
| 61 | +Truthiness |
| 62 | + |
| 63 | + Predicates `.is_true()` and `.is_false()` match *only* `True` and `False`. |
| 64 | + For `.is_truthy()` and `.is_falsy()`, `(starlark.Value).Truth() bool` is used. |
| 65 | + |
| 66 | + assert.that(True).is_true() |
| 67 | + assert.that(False).is_false() |
| 68 | + assert.that(1).is_true() # fails |
| 69 | + assert.that(0).is_false() # fails |
| 70 | + assert.that(None).is_true() # fails |
| 71 | + assert.that(None).is_false() # fails |
| 72 | + assert.that(True).is_truthy() |
| 73 | + assert.that(False).is_falsy() |
| 74 | + assert.that(1).is_truthy() |
| 75 | + assert.that(0).is_falsy() |
| 76 | + assert.that(None).is_truthy() # fails |
| 77 | + assert.that(None).is_falsy() |
| 78 | + |
| 79 | +Strings |
| 80 | + |
| 81 | + assert.that("abc").has_length(3) |
| 82 | + assert.that("abc").starts_with("a") |
| 83 | + assert.that("abc").ends_with("c") |
| 84 | + assert.that("abc").matches("a.+") # prepends "^" to regexp |
| 85 | + assert.that("abc").does_not_match("b.+") # prepends "^" to regexp |
| 86 | + assert.that("abc").contains_match("b.+") |
| 87 | + assert.that("abc").does_not_contain_match("c.+") |
| 88 | + |
| 89 | +Numbers |
| 90 | + |
| 91 | + assert.that(a).is_zero() |
| 92 | + assert.that(a).is_non_zero() |
| 93 | + assert.that(a).is_positive_infinity() |
| 94 | + assert.that(a).is_not_positive_infinity() |
| 95 | + assert.that(a).is_negative_infinity() |
| 96 | + assert.that(a).is_not_negative_infinity() |
| 97 | + assert.that(a).is_finite() |
| 98 | + assert.that(a).is_not_finite() |
| 99 | + assert.that(a).is_nan() |
| 100 | + assert.that(a).is_not_nan() |
| 101 | + assert.that(a).is_within(delta).of(b) |
| 102 | + assert.that(a).is_not_within(delta).of(b) |
| 103 | + |
| 104 | +Lists, strings, and other iterables |
| 105 | + |
| 106 | + `cmp(x,y)` should return negative if x < y, zero if x == y and positive if x > y. |
| 107 | + *Ordered* means that the iterable's elements must increase (or decrease, |
| 108 | + depending on `cmp`) from beginning to end. Adjacent elements are allowed to be equal. |
| 109 | + *Strictly ordered* means that in addition, the elements must be unique |
| 110 | + (i.e. monotonically increasing or decreasing). |
| 111 | + |
| 112 | + assert.that(a).has_size(n) |
| 113 | + assert.that(a).is_empty() |
| 114 | + assert.that(a).is_not_empty() |
| 115 | + assert.that(a).contains(b) |
| 116 | + assert.that(a).does_not_contain(b) |
| 117 | + assert.that(a).contains_all_of(b, c) |
| 118 | + assert.that(a).contains_all_in([b, c]) |
| 119 | + assert.that(a).contains_any_of(b, c) |
| 120 | + assert.that(a).contains_any_in([b, c]) |
| 121 | + assert.that(a).contains_exactly(b, c) |
| 122 | + assert.that(sorted(a)).contains_exactly_elements_in(sorted(b)).in_order() |
| 123 | + assert.that(a).contains_none_of(b, c) |
| 124 | + assert.that(a).contains_none_in([b, c]) |
| 125 | + assert.that(a).contains_no_duplicates() |
| 126 | + assert.that(a).is_ordered() |
| 127 | + assert.that(a).is_ordered_according_to(cmp) |
| 128 | + assert.that(a).is_strictly_ordered() |
| 129 | + assert.that(a).is_strictly_ordered_according_to(cmp) |
| 130 | + |
| 131 | +Asserting order |
| 132 | + |
| 133 | + By default, `.contains_all...` and `.contains_exactly...` do not enforce that the |
| 134 | + order of the elements in the subject under test matches that of the expected |
| 135 | + value. To do that, append `.in_order()` to the returned predicate. |
| 136 | + |
| 137 | + assert.that([2, 4, 6]).contains_all_of(6, 2) |
| 138 | + assert.that([2, 4, 6]).contains_all_of(6, 2).in_order() # fails |
| 139 | + assert.that([2, 4, 6]).contains_all_of(2, 6).in_order() |
| 140 | + assert.that((1, 2, 3)).contains_all_in((1, 3)).in_order() |
| 141 | + assert.that([2, 4, 6]).contains_exactly(2, 6, 4) |
| 142 | + assert.that([2, 4, 6]).contains_exactly(2, 6, 4).in_order() # fails |
| 143 | + assert.that([2, 4, 6]).contains_exactly(2, 4, 6).in_order() |
| 144 | + assert.that((1, 2, 3)).contains_exactly_elements_in([1, 2, 3]).in_order() |
| 145 | + |
| 146 | + When using `.in_order()`, ensure that both the subject under test and the expected |
| 147 | + value have a defined order, otherwise the result is undefined. |
| 148 | + For example, `assert.that(aList).contains_exactly_elements_in(aSet).in_order()` |
| 149 | + may or may not succeed, depending on how the `set` implements ordering. |
| 150 | + The builtin set datatype does not implement ordering. |
| 151 | + These assertions *may or may not* succeed: |
| 152 | + |
| 153 | + assert.that((1, 2, 3)).contains_all_in(set([1, 3])).in_order() |
| 154 | + assert.that(set([3, 2, 1])).contains_exactly_elements_in((1, 2, 3)).in_order() |
| 155 | + assert.that({1:2, 3:4}).contains_all_in((1, 3)).in_order() |
| 156 | + |
| 157 | +Dictionaries, in addition to the table above |
| 158 | + |
| 159 | + assert.that(d).contains_key(k) |
| 160 | + assert.that(d).does_not_contain_key(k) |
| 161 | + assert.that(d).contains_item(k, v) |
| 162 | + assert.that(d).does_not_contain_item(k, v) |
| 163 | + assert.that(d).contains_exactly(k1, v1, k2, v2) |
| 164 | + assert.that(d1).contains_exactly_items_in(d2) |
| 165 | + assert.that(d1.items()).contains_all_in(d2.items()) |
| 166 | + |
| 167 | +Notes (in no particular order): |
| 168 | + |
| 169 | + `None` is not comparable (as in Python 3), so assertions involving `<` `>` |
| 170 | + `<=` `>=` on `None` such as `assert.that(a).is_greater_than(None)` |
| 171 | + fail with `InvalidAssertion` error. |
| 172 | + It is recommended to first check the `None`-ness of values with `.is_none()` |
| 173 | + or `.is_not_none()` before performing inequility assertions. |
| 174 | + |
| 175 | + As in Python, `0`, `0.0` and `-0.0` all compare equal. The assertions |
| 176 | + `.is_zero()` and `.is_non_zero()` are provided for semantic convenience. |
| 177 | + |
| 178 | + Starlark strings are not iterable (unlike Python's) but are iterated on as |
| 179 | + slices of utf-8 runes when needed in this implementation. This works: |
| 180 | + assert.that("abcdefg").contains_all_of("a", "c", "e").in_order() |
| 181 | + assert.that("abcdefg").is_strictly_ordered() |
| 182 | + |
| 183 | + In `.contains...()` assertions a "duplicate values counter" is used that |
| 184 | + relies on the `(starlark.Value).String() string` reprensentation of values |
| 185 | + instead of `(starlark.Value).Hash() (uint32, error)` as some basic types |
| 186 | + are not hashable (list, dict, set). |
| 187 | + For this reason **it is recommended to only pass values of the main data types |
| 188 | + built in to the interpreter to functions of this package:** |
| 189 | + * NoneType |
| 190 | + * bool |
| 191 | + * int |
| 192 | + * float |
| 193 | + * string |
| 194 | + * list |
| 195 | + * tuple |
| 196 | + * dict |
| 197 | + * set |
| 198 | + * function |
| 199 | + * builtin_function_or_method |
| 200 | + |
| 201 | + It is possible to incorrectly express assertions (see all but the last line in |
| 202 | + this block): |
| 203 | + assert.that(x) |
| 204 | + assert.that(x).named(z) |
| 205 | + assert.that(x).is_within(y) |
| 206 | + assert.that(x).is_not_within(y) |
| 207 | + assert.that(x).is_not_within(y).of(z) |
| 208 | + This is why each call to `.that(...)` first checks that no non-terminated |
| 209 | + assertions were previously executed in the current thread. |
| 210 | + A `Close(*starlark.Thread) error` function is also provided to ensure |
| 211 | + this property holds after the interpreter returns. |
| 212 | + |
| 213 | + This library is threadsafe; you may execute multiple assertions in parallel. |
| 214 | + |
| 215 | +const Default = "assert" |
| 216 | +var LocalThreadKeyForClose = Default |
| 217 | +func Asserted(th *starlark.Thread) bool |
| 218 | +func Close(th *starlark.Thread) (err error) |
| 219 | +func NewModule(predeclared starlark.StringDict) |
| 220 | +func That(thread *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, ...) (starlark.Value, error) |
| 221 | +type InvalidAssertion string |
| 222 | +type T struct{ ... } |
| 223 | +type TruthAssertion string |
| 224 | +type UnhandledError struct{ ... } |
| 225 | +type UnresolvedError string |
0 commit comments