Documentation ¶
Overview ¶
Package starlarktruth defines builtins and methods to express test assertions within Starlark programs in the fashion of https://truth.dev
This package is a Starlark port of PyTruth (2c3717ddad 2021-03-10) https://github.com/google/pytruth
The Starlark:
assert.that(a).is_equal_to(b) assert.that(c).named("my value").is_true() assert.that(d).contains(a) assert.that(d).contains_all_of(a, b).in_order() assert.that(d).contains_any_of(a, b, c)
is equivalent to the following Python:
from truth.truth import AssertThat AssertThat(a).IsEqualTo(b) AssertThat(c).Named("my value").IsTrue() AssertThat(d).Contains(a) AssertThat(d).ContainsAllOf(a, b).InOrder() AssertThat(d).ContainsAnyOf(a, b, c)
Often, tests assert a relationship between a value produced by the test (the "actual" value) and some reference value (the "expected" value). It is strongly recommended that the actual value is made the subject of the assertion. For example:
assert.that(actual).is_equal_to(expected) # Recommended. assert.that(expected).is_equal_to(actual) # Not recommended. assert.that(actual).is_in(expected_possibilities) # Recommended. assert.that(expected_possibilities).contains(actual) # Not recommended.
Some assertions
assert.that(a).is_equal_to(b) assert.that(a).is_not_equal_to(b) assert.that(a).is_truthy() assert.that(a).is_falsy() assert.that(a).is_true() assert.that(a).is_false() assert.that(a).is_none() assert.that(a).is_not_none() assert.that(a).is_in(b) assert.that(a).is_any_of(b, c, d) assert.that(a).is_not_in(b) assert.that(a).is_none_of(b, c, d) assert.that(a).is_of_type(type(b)) assert.that(a).is_not_of_type(type(b)) assert.that(a).has_attribute(b) assert.that(a).does_not_have_attribute(b) assert.that(a).is_callable() assert.that(a).is_not_callable() assert.that(a).is_less_than(b) assert.that(a).is_greater_than(b) assert.that(a).is_at_most(b) assert.that(a).is_at_least(b)
Truthiness
Predicates `.is_true()` and `.is_false()` match *only* `True` and `False`. For `.is_truthy()` and `.is_falsy()`, `(starlark.Value).Truth() bool` is used. assert.that(True).is_true() assert.that(False).is_false() assert.that(1).is_true() # fails assert.that(0).is_false() # fails assert.that(None).is_true() # fails assert.that(None).is_false() # fails assert.that(True).is_truthy() assert.that(False).is_falsy() assert.that(1).is_truthy() assert.that(0).is_falsy() assert.that(None).is_truthy() # fails assert.that(None).is_falsy()
Strings
assert.that("abc").has_length(3) assert.that("abc").starts_with("a") assert.that("abc").ends_with("c") assert.that("abc").matches("a.+") # prepends "^" to regexp assert.that("abc").does_not_match("b.+") # prepends "^" to regexp assert.that("abc").contains_match("b.+") assert.that("abc").does_not_contain_match("c.+")
Numbers
assert.that(a).is_zero() assert.that(a).is_non_zero() assert.that(a).is_positive_infinity() assert.that(a).is_not_positive_infinity() assert.that(a).is_negative_infinity() assert.that(a).is_not_negative_infinity() assert.that(a).is_finite() assert.that(a).is_not_finite() assert.that(a).is_nan() assert.that(a).is_not_nan() assert.that(a).is_within(delta).of(b) assert.that(a).is_not_within(delta).of(b)
Lists, strings, and other iterables
`cmp(x,y)` should return negative if x < y, zero if x == y and positive if x > y. *Ordered* means that the iterable's elements must increase (or decrease, depending on `cmp`) from beginning to end. Adjacent elements are allowed to be equal. *Strictly ordered* means that in addition, the elements must be unique (i.e. monotonically increasing or decreasing). assert.that(a).has_size(n) assert.that(a).is_empty() assert.that(a).is_not_empty() assert.that(a).contains(b) assert.that(a).does_not_contain(b) assert.that(a).contains_all_of(b, c) assert.that(a).contains_all_in([b, c]) assert.that(a).contains_any_of(b, c) assert.that(a).contains_any_in([b, c]) assert.that(a).contains_exactly(b, c) assert.that(sorted(a)).contains_exactly_elements_in(sorted(b)).in_order() assert.that(a).contains_none_of(b, c) assert.that(a).contains_none_in([b, c]) assert.that(a).contains_no_duplicates() assert.that(a).is_ordered() assert.that(a).is_ordered_according_to(cmp) assert.that(a).is_strictly_ordered() assert.that(a).is_strictly_ordered_according_to(cmp)
Asserting order
By default, `.contains_all...` and `.contains_exactly...` do not enforce that the order of the elements in the subject under test matches that of the expected value. To do that, append `.in_order()` to the returned predicate. assert.that([2, 4, 6]).contains_all_of(6, 2) assert.that([2, 4, 6]).contains_all_of(6, 2).in_order() # fails assert.that([2, 4, 6]).contains_all_of(2, 6).in_order() assert.that((1, 2, 3)).contains_all_in((1, 3)).in_order() assert.that([2, 4, 6]).contains_exactly(2, 6, 4) assert.that([2, 4, 6]).contains_exactly(2, 6, 4).in_order() # fails assert.that([2, 4, 6]).contains_exactly(2, 4, 6).in_order() assert.that((1, 2, 3)).contains_exactly_elements_in([1, 2, 3]).in_order() When using `.in_order()`, ensure that both the subject under test and the expected value have a defined order, otherwise the result is undefined. For example, `assert.that(aList).contains_exactly_elements_in(aSet).in_order()` may or may not succeed, depending on how the `set` implements ordering. The builtin set datatype implements insertion ordering. These assertions succeed: assert.that((1, 2, 3)).contains_all_in(set([1, 3])).in_order() assert.that(set([3, 2, 1])).contains_exactly_elements_in((3, 2, 1)).in_order() assert.that({1:2, 3:4}).contains_all_in((1, 3)).in_order()
Dictionaries, in addition to the table above
assert.that(d).contains_key(k) assert.that(d).does_not_contain_key(k) assert.that(d).contains_item(k, v) assert.that(d).does_not_contain_item(k, v) assert.that(d).contains_exactly(k1, v1, k2, v2) assert.that(d1).contains_exactly_items_in(d2) assert.that(d1.items()).contains_all_in(d2.items())
Notes (in no particular order):
`None` is not comparable (as in Python 3), so assertions involving `<` `>` `<=` `>=` on `None` such as `assert.that(a).is_greater_than(None)` fail with `InvalidAssertion` error. It is recommended to first check the `None`-ness of values with `.is_none()` or `.is_not_none()` before performing inequility assertions. As in Python, `0`, `0.0` and `-0.0` all compare equal. The assertions `.is_zero()` and `.is_non_zero()` are provided for semantic convenience. Starlark strings are not iterable (unlike Python's) but are iterated on as slices of utf-8 runes when needed in this implementation. This works: assert.that("abcdefg").contains_all_of("a", "c", "e").in_order() assert.that("abcdefg").is_strictly_ordered() In `.contains...()` assertions a "duplicate values counter" is used that relies on the `(starlark.Value).String() string` reprensentation of values instead of `(starlark.Value).Hash() (uint32, error)` as some basic types are not hashable (list, dict, set). For this reason **it is recommended to only pass values of the main data types built in to the interpreter to functions of this package:** * NoneType * bool * int * float * string * list * tuple * dict * set * function * builtin_function_or_method It is possible to incorrectly express assertions (see all but the last line in this block): assert.that(x) assert.that(x).named(z) assert.that(x).is_within(y) assert.that(x).is_not_within(y) assert.that(x).is_not_within(y).of(z) This is why each call to `.that(...)` first checks that no non-terminated assertions were previously executed in the current thread. A `Close(*starlark.Thread) error` function is also provided to ensure this property holds after the interpreter returns. This library is threadsafe; you may execute multiple assertions in parallel.
Example ¶
package main import ( "fmt" "go.starlark.net/starlark" "github.com/FuzzyMonkeyCo/monkey/pkg/starlarktruth" ) func main() { starlarktruth.Module = "should" starlarktruth.Method = "verify" starlarktruth.NewModule(starlark.Universe) data := ` squares = [x*x for x in range(10)] should.verify(squares).has_size(10) print(greeting + ", world") `[1:] th := &starlark.Thread{ Name: "truth", Print: func(_ *starlark.Thread, msg string) { fmt.Println(msg) }, } _, err := starlark.ExecFile(th, "some/file.star", data, starlark.StringDict{ "greeting": starlark.String("hello"), }) if err != nil { if evalErr, ok := err.(*starlark.EvalError); ok { panic(evalErr.Backtrace()) } panic(err) } // Fail if any subject wasn't applied a property. if err := starlarktruth.Close(th); err != nil { panic(err) } }
Output: hello, world
Index ¶
- Variables
- func Asserted(th *starlark.Thread) bool
- func Close(th *starlark.Thread) (err error)
- func NewModule(predeclared starlark.StringDict)
- func That(thread *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, ...) (starlark.Value, error)
- type InvalidAssertion
- type T
- type TruthAssertion
- type UnhandledError
- type UnresolvedError
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ( // Module is the module name used by default. Module = "assert" // Method is the attribute name used by default. Method = "that" )
var LocalThreadKeyForClose = Module
LocalThreadKeyForClose is used by Close() and internally to check subjects are eventually resolved.
Functions ¶
func Close ¶
Close ensures that all created subjects were eventually resolved. Otherwise it returns an error pinpointing the UnresolvedError position. A subject is considered resolved what at least one proposition has been executed on it. An unresolved or dangling assertion is almost certainly a test author error.
func NewModule ¶
func NewModule(predeclared starlark.StringDict)
NewModule registers a Starlark module of https://truth.dev/
Types ¶
type InvalidAssertion ¶
type InvalidAssertion string
InvalidAssertion signifies an invalid assertion was attempted such as comparing with None.
func (InvalidAssertion) Error ¶
func (e InvalidAssertion) Error() string
type TruthAssertion ¶
type TruthAssertion string
TruthAssertion signifies an assertion predicate was invalidated.
func (TruthAssertion) Error ¶
func (e TruthAssertion) Error() string
type UnhandledError ¶
type UnhandledError struct {
// contains filtered or unexported fields
}
UnhandledError appears when an operation on an incompatible type is attempted.
func (UnhandledError) Error ¶
func (e UnhandledError) Error() string
type UnresolvedError ¶
type UnresolvedError struct { // Msg is the error string Msg string }
UnresolvedError describes that an `assert.that(actual)` was called but never any of its `.truth_methods(subject)`. At the exception of (as each by themselves this still require an assertion): * `.named(name)` * `.is_within(tolerance)` * `.is_not_within(tolerance)`
func (*UnresolvedError) Error ¶
func (e *UnresolvedError) Error() string