Skip to content

Commit 64eec76

Browse files
authored
feat: add testing utilities (#368)
* feat: add testing utils * finalize * typing ext
1 parent e4e46c2 commit 64eec76

File tree

4 files changed

+602
-0
lines changed

4 files changed

+602
-0
lines changed

docs/guides/testing.md

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# Testing with `psygnal`
2+
3+
If you would like to test to ensure that signals are emitted (or not emitted) as
4+
expected, you can use the convenience functions in the
5+
[`psygnal.testing`][psygnal.testing] module.
6+
7+
## Examples
8+
9+
The easiest approach is to use one of the `assert_*` context
10+
managers. These temporarily listen to a signal and check if it is
11+
emitted (or not emitted) when the context is exited. The API
12+
closely mirrors the [`unittest.mock`][unittest.mock.Mock.assert_called] API,
13+
with the word "called" replaced with "emitted".
14+
15+
```python
16+
from psygnal import Signal
17+
import psygnal.testing as pt
18+
19+
class MyObject:
20+
changed = Signal()
21+
value_changed = Signal(int)
22+
23+
def test_my_object():
24+
obj = MyObject()
25+
26+
with pt.assert_emitted(obj.changed):
27+
obj.changed.emit()
28+
29+
with pt.assert_not_emitted(obj.value_changed):
30+
obj.changed.emit()
31+
32+
with pt.assert_emitted_once(obj.value_changed):
33+
obj.value_changed.emit(42)
34+
35+
with pt.assert_emitted_once_with(obj.value_changed, 42):
36+
obj.value_changed.emit(42)
37+
38+
with pt.assert_ever_emitted_with(obj.value_changed, 42):
39+
obj.value_changed.emit(41)
40+
obj.value_changed.emit(42)
41+
obj.value_changed.emit(43)
42+
```
43+
44+
All of the context managers yield an instance of
45+
[`SignalTester`][psygnal.testing.SignalTester], which can be used to check the
46+
number of emissions and the arguments. It may also be used directly:
47+
48+
```python
49+
from psygnal import Signal
50+
import psygnal.testing as pt
51+
52+
class MyObject:
53+
value_changed = Signal(int)
54+
55+
def test_my_object():
56+
obj = MyObject()
57+
tester = pt.SignalTester(obj.value_changed)
58+
59+
with tester:
60+
obj.value_changed.emit(42)
61+
obj.value_changed.emit(43)
62+
63+
assert tester.emit_count == 2
64+
assert tester.emit_args_list == [(42,), (43,)]
65+
assert tester.emit_args == (43,)
66+
tester.assert_ever_emitted_with(42)
67+
tester.assert_emitted_with(43)
68+
```
69+
70+
See API documentation for
71+
[`SignalTester`][psygnal.testing.SignalTester] for more details.

mkdocs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ nav:
2424
- Evented Dataclasses: guides/dataclasses.md
2525
- Evented Pydantic Model: guides/model.md
2626
- Throttling & Debouncing: guides/throttler.md
27+
- Testing: guides/testing.md
2728
- Debugging: guides/debugging.md
2829

2930
theme:

0 commit comments

Comments
 (0)