Skip to content

Commit 2629599

Browse files
authored
docs: add more concrete details and usage instructions to docs (#159)
1 parent 97fb55b commit 2629599

File tree

10 files changed

+244
-161
lines changed

10 files changed

+244
-161
lines changed

README.md

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -68,19 +68,24 @@ For example, using the built-in [unittest][] framework, you would use the `setUp
6868

6969
This basic example assumes you are using [pytest][]. For more detailed documentation, see Decoy's [usage guide][] and [API reference][].
7070

71-
### Define your test
72-
73-
Decoy will add a `decoy` fixture that provides its mock creation API.
71+
Decoy will add a `decoy` fixture to pytest that provides its mock creation API.
7472

7573
```python
7674
from decoy import Decoy
77-
from todo import TodoAPI, TodoItem
78-
from todo.store TodoStore
7975

80-
def test_add_todo(decoy: Decoy) -> None:
76+
def test_something(decoy: Decoy) -> None:
8177
...
8278
```
8379

80+
!!! note
81+
82+
Importing the `Decoy` interface for type annotations is recommended, but optional. If your project does not use type annotations, you can simply write:
83+
84+
```python
85+
def test_something(decoy):
86+
...
87+
```
88+
8489
### Create a mock
8590

8691
Use `decoy.mock` to create a mock based on some specification. From there, inject the mock into your test subject.

decoy/core.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,11 @@ def __init__(
3333
self._warning_checker = warning_checker or WarningChecker()
3434
self._stub_store = stub_store or StubStore()
3535
self._spy_log = spy_log or SpyLog()
36-
self._call_hander = call_handler or CallHandler(
36+
self._call_handler = call_handler or CallHandler(
3737
spy_log=self._spy_log,
3838
stub_store=self._stub_store,
3939
)
40-
self._spy_creator = spy_creator or SpyCreator(call_handler=self._call_hander)
40+
self._spy_creator = spy_creator or SpyCreator(call_handler=self._call_handler)
4141

4242
def mock(
4343
self,

decoy/errors.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212

1313
class MockNameRequiredError(ValueError):
14-
"""An error reaised if a name is not provided for a mock.
14+
"""An error raised if a name is not provided for a mock.
1515
1616
See the [MockNameRequiredError guide][] for more details.
1717

decoy/matchers.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
"""Matcher helpers.
22
33
A "matcher" is a class with an `__eq__` method defined. Use them anywhere
4-
in your test where you would use an actual value for equality (`==`) comparision.
4+
in your test where you would use an actual value for equality (`==`) comparison.
55
66
Matchers help you loosen assertions where strict adherence to an exact value
7-
is not relevent to what you're trying to test.
7+
is not relevant to what you're trying to test.
88
99
!!! example
1010
```python

docs/advanced/properties.md

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -30,26 +30,26 @@ def test(decoy: Decoy) -> None:
3030
If you would like to stub a return value for a property that is different than the default behavior, simply use the property itself as your rehearsal.
3131

3232
```python
33-
dep = decoy.mock()
33+
dependency = decoy.mock(name="dependency")
3434

3535
decoy.when(
36-
dep.some_property # <- "rehearsal" of a property getter
36+
dependency.some_property # <- "rehearsal" of a property getter
3737
).then_return(42)
3838

3939
assert dep.some_property == 42
4040
```
4141

42-
You can also configure any other stubbing, like raising an error.
42+
You can also configure any other behavior, like raising an error.
4343

4444
```python
45-
dep = decoy.mock()
45+
dependency = decoy.mock(name="dependency")
4646

4747
decoy.when(
48-
dep.some_property
48+
dependency.some_property
4949
).then_raise(RuntimeError("oh no"))
5050

5151
with pytest.raises(RuntimeError, match="oh no"):
52-
dep.some_property
52+
dependency.some_property
5353
```
5454

5555
### Stubbing a setter or deleter
@@ -59,21 +59,21 @@ While you cannot stub a return value for a getter or setter, you can stub a `rai
5959
The `prop` method allows you to create rehearsals of setters and deleters. Use [decoy.Prop.set][] to create a setter rehearsal, and [decoy.Prop.delete][] to create a deleter rehearsal.
6060

6161
```python
62-
dep = decoy.mock()
62+
dependency = decoy.mock(name="dependency")
6363

6464
decoy.when(
65-
decoy.prop(dep.some_property).set(42)
65+
decoy.prop(dependency.some_property).set(42)
6666
).then_raise(RuntimeError("oh no"))
6767

6868
decoy.when(
69-
decoy.prop(dep.some_property).delete()
69+
decoy.prop(dependency.some_property).delete()
7070
).then_raise(RuntimeError("what a disaster"))
7171

7272
with pytest.raises(RuntimeError, match="oh no"):
73-
dep.some_property = 42
73+
dependency.some_property = 42
7474

7575
with pytest.raises(RuntimeError, match="what a disaster"):
76-
del dep.some_property
76+
del dependency.some_property
7777
```
7878

7979
!!! tip
@@ -90,19 +90,19 @@ Mocking and verifying property setters and deleters is most useful for testing c
9090

9191
!!! tip
9292

93-
You cannot verify getters with `Decoy.verify`. The `verify` method is for verifying side-effects, and it is the opinion of the author that property getters should not trigger side-effects. Getter-triggered side effects are confusing and do not communicate the design intent of a system.
93+
You cannot `verify` getters. The `verify` method is for verifying side-effects, and it is the opinion of the author that property getters should not trigger side-effects. Getter-triggered side effects are confusing and do not communicate the design intent of a system.
9494

9595
### Verifying a setter
9696

9797
Use [decoy.Prop.set][] to create a setter rehearsal to use in [decoy.Decoy.verify][].
9898

9999
```python
100-
dep = decoy.mock()
100+
dependency = decoy.mock(name="dependency")
101101

102-
dep.some_property = 42
102+
dependency.some_property = 42
103103

104104
decoy.verify(
105-
decoy.prop(dep.some_property).set(42) # <- "rehearsal" of a property setter
105+
decoy.prop(dependency.some_property).set(42) # <- "rehearsal" of a property setter
106106
)
107107
```
108108

@@ -111,12 +111,12 @@ decoy.verify(
111111
Use [decoy.Prop.delete][] to create a deleter rehearsal to use in [decoy.Decoy.verify][].
112112

113113
```python
114-
dep = decoy.mock()
114+
dependency = decoy.mock(name="dependency")
115115

116-
del dep.some_property
116+
del dependency.some_property
117117

118118
decoy.verify(
119-
decoy.prop(dep.some_property).delete() # <- "rehearsal" of a property deleter
119+
decoy.prop(dependency.some_property).delete() # <- "rehearsal" of a property deleter
120120
)
121121
```
122122

docs/usage/create.md

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,36 @@
22

33
Decoy mocks are flexible objects that can be used in place of a class instance or a callable object, like a function. Mocks are created using the [decoy.Decoy.mock][] method.
44

5+
## Default behaviors
6+
7+
Decoy mock objects are flexible, callable proxy objects that simply record interactions made with the object. Accessing any property of the mock will return a child mock, and calling the mock itself or any "method" of the mock will return `None`.
8+
9+
```python
10+
my_mock = decoy.mock(name="my_mock")
11+
12+
assert my_mock() is None
13+
assert my_mock.some_method("hello world") is None
14+
assert my_mock.some_property.some_method("hey") is None
15+
```
16+
17+
You can configure a mock's behaviors using [decoy.when](./when.md). You can make assertions about how a mock was called using [decoy.verify](./verify.md).
18+
519
## Mocking a class
620

7-
To mock a class instance, pass the `cls` argument to `decoy.mock`. Decoy will inspect type annotations and method signatures to automatically configure a name for use in assertion messages and methods as synchronous or asynchronous. Decoy mocks are automatically deep.
21+
To mock a class instance, pass the `cls` argument to `decoy.mock`. Decoy will inspect type annotations and method signatures to set a name for use in assertion messages, configure methods as synchronous or asynchronous, and understand function keyword arguments.
822

923
```python
10-
def test_my_thing(decoy: Decoy) -> None:
11-
some_dependency = decoy.mock(cls=SomeDependency)
24+
some_dependency = decoy.mock(cls=SomeDependency)
1225
```
1326

1427
To type checkers, the mock will appear to have the exact same type as the `cls` argument. The mock will also pass `isinstance` checks.
1528

1629
## Mocking a function
1730

18-
To mock a function, pass the `func` argument to `decoy.mock`. Decoy will inspect `func` to automatically configure a name for use in assertion messages and set the function as synchronous or asynchronous.
31+
To mock a function, pass the `func` argument to `decoy.mock`. Decoy will inspect `func` to set a name for use in assertion messages, configure the mock as synchronous or asynchronous, and understand function keyword arguments.
1932

2033
```python
21-
def test_my_thing(decoy: Decoy) -> None:
22-
mock_function = decoy.mock(func=some_function)
34+
mock_function = decoy.mock(func=some_function)
2335
```
2436

2537
To type checkers, the mock will appear to have the exact same type as the `func` argument. The function mock will pass `inspect.signature` checks.
@@ -31,7 +43,6 @@ You can call `decoy.mock` without using `cls` or `func`. A spec-less mock is use
3143
When creating a mock without a spec, you must use the `name` argument to give the mock a name to use in assertion messages. You must use the `is_async` argument if the created mock will be used as an asynchronous callable.
3244

3345
```python
34-
def test_my_thing(decoy: Decoy) -> None:
35-
callback = decoy.mock(name="callback")
36-
async_callback = decoy.mock(name="async_callback", is_async=True)
46+
callback = decoy.mock(name="callback")
47+
async_callback = decoy.mock(name="async_callback", is_async=True)
3748
```

docs/usage/errors-and-warnings.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Decoy's job as a mocking library is to provide you, the user, with useful design
66

77
### VerifyError
88

9-
A [decoy.errors.VerifyError][] will be raised if a call to [decoy.Decoy.verify][] does not match the given rehearsal. This is a normal assertion, and means your code under test isn't behaving according to your tests specifications.
9+
A [decoy.errors.VerifyError][] will be raised if a call to [decoy.Decoy.verify][] does not match the given rehearsal. This is a normal assertion, and means your code under test isn't behaving according to your test's specification.
1010

1111
```python
1212
func = decoy.mock()
@@ -57,7 +57,6 @@ If you pass `cls` or `func`, Decoy will infer the mock's name - to be used in as
5757
my_mock = decoy.mock(name="my_mock")
5858
```
5959

60-
6160
## Warnings
6261

6362
Decoy uses Python's [warnings system][] to provide feedback about dubious mock usage that isn't _technically_ incorrect. These warnings won't fail your tests, but you probably want to fix them.

docs/usage/matchers.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,19 @@ Sometimes, when you're stubbing or verifying calls (or really when you're doing
44

55
Decoy includes the [decoy.matchers][] module, which is a set of Python classes with `__eq__` methods defined that you can use in rehearsals and/or assertions in place of actual values
66

7+
## Available matchers
8+
9+
| Matcher | Description |
10+
| --------------------------------- | ---------------------------------------------------- |
11+
| [decoy.matchers.Anything][] | Matches any value that isn't `None` |
12+
| [decoy.matchers.DictMatching][] | Matches a `dict` based on some of its values |
13+
| [decoy.matchers.ErrorMatching][] | Matches an `Exception` based on its type and message |
14+
| [decoy.matchers.HasAttributes][] | Matches an object based on its attributes |
15+
| [decoy.matchers.IsA][] | Matches using `isinstance` |
16+
| [decoy.matchers.IsNot][] | Matches anything that isn't a given value |
17+
| [decoy.matchers.StringMatching][] | Matches a string against a regular expression |
18+
| [decoy.matchers.Captor][] | Captures the comparison value (see below) |
19+
720
## Basic usage
821

922
To use, import `decoy.matchers` and use a matcher wherever you would normally use a value.

0 commit comments

Comments
 (0)