You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: _posts/2020-01-25-testing_external_api_calls.md
+65-5Lines changed: 65 additions & 5 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -277,8 +277,16 @@ have one of these each time we introduce a new option.
277
277
278
278
## SUGGESTION: Build an Adapter (a wrapper for the external API)
279
279
280
+
We really want to disentangle our business logic from our API integration.
281
+
Building an abstraction, a wrapper around the API that just exposes nice,
282
+
readable methods for us to call in our code.
283
+
284
+
> We call it an "adapter" in [ports & adapters](https://github.com/cosmicpython/book/blob/master/chapter_02_repository.asciidoc#what_is_a_port_and_what_is_an_adapter) sense,
285
+
> but you don't have to go full-on hexagonal architecture to use
286
+
> this pattern.
287
+
288
+
280
289
281
-
You'll probably tend towards
282
290
```python
283
291
classRealCargoAPI:
284
292
API_URL='https://example.org'
@@ -562,6 +570,14 @@ or they might be a useful backup option if proper integration tests aren't
562
570
possible. In a similar way, you probably want ways of _selectively_ running
563
571
your contract tests against your third party.
564
572
573
+
> you can also run your contract tests against your fake api.
574
+
575
+
When you run your contract tests against your own fake api as well as
576
+
against the real thing, you're confirming the quality of your fake.
577
+
Some people call this [verified fakes](https://pythonspeed.com/articles/verified-fakes/)
578
+
(see also ["stop mocking and start testing"](https://nedbatchelder.com/blog/201206/tldw_stop_mocking_start_testing.html).)
@@ -695,12 +711,56 @@ The design pressure is the killer argument in our opinion. Because hand-rolling
695
711
a fake _is_ more effort, it forces us to think about the API of our adapter,
696
712
and it gives us an incentive to keep it simple.
697
713
698
-
(callback to initial decision to build a wrapper. the fake helps do it right)
714
+
If you think back to our initial decision to build a wrapper, in our toy example
715
+
it was quite easy to decide what the adapter should look like, we just needed
716
+
one public method called `sync()`. In real life it's sometimes harder to figure
717
+
out what belongs in an adapter, and what stays in business logic. By forcing
718
+
ourselves to build a fake, we get to really see the shape of the thing that
719
+
we're abstracting out.
720
+
721
+
* See this excerpt from our book in which we talk about
722
+
[heuristics for abstracting out dependencies](http://www.obeythetestinggoat.com/new-book-excerpt-abstractions.html).
723
+
724
+
725
+
> For bonus points, you can even share code between the fake class you use
726
+
>for your unit tests, and the fake you use for your integration tests.
727
+
728
+
729
+
730
+
731
+
## Recap
732
+
733
+
* As soon as your integration with an external API gets beyond the trivial,
734
+
mocking and patching starts to be quite painful
735
+
736
+
* Consider abstracting out a wrapper around your API
737
+
738
+
* Use integration tests to test your adapter, and unit tests for your
739
+
business logic (and to check that you call your adapter correctly)
740
+
741
+
* Consider writing your own fakes for your unit tests. They will
742
+
help you find a good abstraction.
743
+
744
+
* If you want a way for devs orCI to run tests without depending
745
+
on the external API, consider also writing a fully-functional fake of the
746
+
third-party API (an actual web server).
747
+
748
+
* For bonus points, the two fakes can share code.
749
+
750
+
* Selectively running integration tests against both the fake and the real API
751
+
can validate that both continue to work over time.
752
+
753
+
* You could also consider adding more targeted "contract tests"for this purpose.
754
+
699
755
700
-
* link to [example code](https://github.com/cosmicpython/code/tree/blogpost-testing-api)
756
+
> If you'd like to play around with the code from this blog post, you can
757
+
> [check it out here](https://github.com/cosmicpython/code/tree/blogpost-testing-api)
701
758
702
759
703
-
# TODOS:
760
+
## Prior art
704
761
705
-
* link to Brandon [Hoist your I/O](https://www.youtube.com/watch?v=PBQN62oUnN8) video?
762
+
* [Mock Hell](https://www.youtube.com/watch?v=CdKaZ7boiZ4) by Ed Jung
763
+
* [stop mocking and start testing](https://nedbatchelder.com/blog/201206/tldw_stop_mocking_start_testing.html) by Augie Fackler Nathaniel Manista (and summarised by Ned Batchelder)
764
+
* [verified fakes](https://pythonspeed.com/articles/verified-fakes/) by Itamar Turner-Trauring
765
+
* [Hoist your I/O](https://www.youtube.com/watch?v=PBQN62oUnN8) by Brandon Rhodes
0 commit comments