Good unit tests exercise the inputs, interactions, and outputs of a system. In simple cases, testing a function can be as simple as invoking the it with a value, and then you assert the value returned is what you expect.
In more complex examples, your function has dependencies, or interactions, on other functions. Dependencies can be interactions with your own system, internal APIs, or 3rd party APIs. While we could control our own system, we don’t want to depend on a 3rd party API being up while we run our unit tests. Mocking out these dependencies is how we can control the environment in our tests.
I like the Python HTTPretty tool for mocking out 3rd party API responses. In summary, it allows you to configure mock responses for HTTP at the socket level. That means your code will exercise the pathways all the way down to the network request.
Our applications commonly consume more internal and external APIs. In dynamic languages like Ruby or Python, I’ve found these best practices useful.
- Create an API client that speaks your domain language. APIs expose their own vocabulary that might not match up to yours. Your API client should use terminology that matches your domain. It should accept objects that your application uses (e.g. User, Message). The API client’s responsibility is translating your domain objects to the API.
- Don’t return dictionaries or hashmaps from your client. It’s quick, easy and tempting to return the parsed JSON response as a dict or hashmap. Just as our API client exposes functions that accept our domain objects as arguments, and we should return responses in our domain objects.
- Thoroughly unit test your API client in isolation down to the HTTP response. This includes checking HTTP response code, and parsing the HTTP response. After that, I find it easier to use object mocking in your other unit tests that use the API client.
For some other best practices, see the slides for my BostonRB August 2014 Lightning Talk, “Don’t let SOA get in your way.“