3.2.6. effect.testing module¶
Various functions and dispatchers for testing effects.
Usually the best way to test effects is by using effect.sync_perform()
with a SequenceDispatcher
.
-
class
effect.testing.
SequenceDispatcher
(sequence)¶ Bases:
object
A dispatcher which steps through a sequence of (intent, func) tuples and runs
func
to perform intents in strict sequence.So, if you expect to first perform an intent like
MyIntent('a')
and then an intent likeOtherIntent('b')
, you can create and use a dispatcher like this:sequence = SequenceDispatcher([ (MyIntent('a'), lambda i: 'my-intent-result'), (OtherIntent('b'), lambda i: 'other-intent-result') ]) with sequence.consume(): sync_perform(sequence, eff)
It’s important to use with sequence.consume(): to ensure that all of the intents are performed. Otherwise, if your code has a bug that causes it to return before all effects are performed, your test may not fail.
None
is returned if the next intent in the sequence is not equal to the intent being performed, or if there are no more items left in the sequence (this is standard behavior for dispatchers that don’t handle an intent). This lets this dispatcher be composed easily with others.Parameters: sequence (list) – Sequence of (intent, fn). -
consume
(*args, **kwds)¶ Return a context manager that can be used with the with syntax to ensure that all steps are performed by the end.
-
consumed
()¶ Return True if all of the steps were performed.
-
-
class
effect.testing.
EQDispatcher
(mapping)¶ Bases:
object
An equality-based (constant) dispatcher.
This dispatcher looks up intents by equality and performs them by returning an associated constant value.
This is sometimes useful, but
SequenceDispatcher
should be preferred, since it constrains the order of effects, which is usually important.Users provide a mapping of intents to results, where the intents are matched against the intents being performed with a simple equality check (not a type check!).
The mapping must be provided as a sequence of two-tuples. We don’t use a dict because we don’t want to require that the intents be hashable (in practice a lot of them aren’t, and it’s a pain to require it). If you want to construct your mapping as a dict, you can, just pass in the result of
d.items()
.e.g.:
>>> sync_perform(EQDispatcher([(MyIntent(1, 2), 'the-result')]), ... Effect(MyIntent(1, 2))) 'the-result'
assuming MyIntent supports
__eq__
by value.Parameters: mapping (list) – A sequence of tuples of (intent, result).
-
class
effect.testing.
EQFDispatcher
(mapping)¶ Bases:
object
An Equality-based function dispatcher.
This dispatcher looks up intents by equality and performs them by invoking an associated function.
This is sometimes useful, but
SequenceDispatcher
should be preferred, since it constrains the order of effects, which is usually important.Users provide a mapping of intents to functions, where the intents are matched against the intents being performed with a simple equality check (not a type check!). The functions in the mapping will be passed only the intent and are expected to return the result or raise an exception.
The mapping must be provided as a sequence of two-tuples. We don’t use a dict because we don’t want to require that the intents be hashable (in practice a lot of them aren’t, and it’s a pain to require it). If you want to construct your mapping as a dict, you can, just pass in the result of
d.items()
.e.g.:
>>> sync_perform( ... EQFDispatcher([( ... MyIntent(1, 2), lambda i: 'the-result')]), ... Effect(MyIntent(1, 2))) 'the-result'
assuming MyIntent supports
__eq__
by value.Parameters: mapping (list) – A sequence of two-tuples of (intent, function).
-
effect.testing.
resolve_effect
(effect, result, is_error=False)¶ Supply a result for an effect, allowing its callbacks to run.
Note that is a pretty low-level testing utility; it’s much better to use a higher-level tool like
SequenceDispatcher
in your tests.The return value of the last callback is returned, unless any callback returns another Effect, in which case an Effect representing that operation plus the remaining callbacks will be returned.
This allows you to test your code in a somewhat “channel”-oriented way:
eff = do_thing() next_eff = resolve_effect(eff, first_result) next_eff = resolve_effect(next_eff, second_result) result = resolve_effect(next_eff, third_result)Equivalently, if you don’t care about intermediate results:
result = resolve_effect( resolve_effect( resolve_effect( do_thing(), first_result), second_result), third_result)
NOTE: parallel effects have no special support. They can be resolved with a sequence, and if they’re returned from another effect’s callback they will be returned just like any other effect.
Parameters: - is_error (bool) – Indicate whether the result should be treated as an exception or a regular result.
- result – If
is_error
is False, this can be any object and will be treated as the result of the effect. Ifis_error
is True, this must be a three-tuple in the style ofsys.exc_info
.
-
effect.testing.
fail_effect
(effect, exception)¶ Resolve an effect with an exception, so its error handler will be run.
-
class
effect.testing.
Stub
(intent)¶ Bases:
object
DEPRECATED in favor of using
SequenceDispatcher
.An intent which wraps another intent, to flag that the intent should be automatically resolved by
resolve_stub()
.Stub
is intentionally not performable by any default mechanism.-
intent
= Attribute(name='intent', default=NOTHING, validator=None, repr=True, cmp=True, hash=True, init=True)¶
-
-
effect.testing.
ESConstant
(x)¶ DEPRECATED. Return Effect(Stub(Constant(x)))
-
effect.testing.
ESError
(x)¶ DEPRECATED. Return Effect(Stub(Error(x)))
-
effect.testing.
ESFunc
(x)¶ DEPRECATED. Return Effect(Stub(Func(x)))
-
effect.testing.
resolve_stubs
(dispatcher, effect)¶ DEPRECATED in favor of obj:SequenceDispatcher.
Successively performs effects with resolve_stub until a non-Effect value, or an Effect with a non-stub intent is returned, and return that value.
Parallel effects are supported by recursively invoking resolve_stubs on the child effects, if all of their children are stubs.