Developer advocate / Data Scientist - support open-source and building the community.
How to Mock Well in Tests
Traps and Pitfalls When Using Mock and Pytest
Grab the slides:
Do you write tests?
What is Unit test
Check if matches expected output
✅ or ❌
pytest is a framework that makes building simple and scalable tests easy.
Perfect for unit test, also have other features via fixtures: patching, mocking, parameterize...
pip install -U pytest pytest --version
import pytest def serve_beer(age): if (age is None) or (age<18): return "No beer" else: return "Have beer" def test_serve_beer_legal(): adult = 25 assert serve_beer(adult) == "Have beer" def test_serve_beer_illegal(): child = 10 assert serve_beer(child) == "No beer"
To run it, in the ternimal:
Let's see it in action!!!
import pytest def test_zero_division(): with pytest.raises(ZeroDivisionError): 1 / 0
import pytest def myfunc(): raise ValueError("Exception 123 raised") def test_match(): with pytest.raises(ValueError, match=r".* 123 .*"): myfunc()
there is also another Way (xfail)
Making a "unit" for test is not always easy
Monkey patching in Pytest
Monkey patching is replacing a function/method/class by another at runtime, for testing purpses, fixing a bug or otherwise changing behaviour.
It is useful when your test involve some external funtions (e.g. getting a result from the backend with your API call)
monkeypatch.setattr(obj, name, value, raising=True) monkeypatch.delattr(obj, name, raising=True) monkeypatch.setitem(mapping, name, value) monkeypatch.delitem(obj, name, raising=True) monkeypatch.setenv(name, value, prepend=False) monkeypatch.delenv(name, raising=True) monkeypatch.syspath_prepend(path) monkeypatch.chdir(path)
from pathlib import Path def getssh(): """Simple function to return expanded homedir ssh path.""" return Path.home() / ".ssh" def test_getssh(monkeypatch): # mocked return function to replace Path.home # always return '/abc' def mockreturn(): return Path("/abc") # Application of the monkeypatch to replace Path.home # with the behavior of mockreturn defined above. monkeypatch.setattr(Path, "home", mockreturn) # Calling getssh() will use mockreturn in place of Path.home # for this test with the monkeypatch. x = getssh() assert x == Path("/abc/.ssh")
Note that in the example, you have to write your own mockreturn function. With a more complex object (requests library objs), it could be quite complicated to create a mock object yourself.
The mock library gives you an object you can use to monkey-patch. The mock object from the mock library also gives you excellent features out of the box that helps you test that the mock behaves a certain way.
from unittest import mock import requests from requests.exceptions import HTTPError @mock.patch('requests.get') def test_google_query(self, mock_get): """test google query method""" mock_resp = self._mock_response(content="ELEPHANTS") mock_get.return_value = mock_resp result = google_query('elephants') self.assertEqual(result, 'ELEPHANTS') self.assertTrue(mock_resp.raise_for_status.called)
- Invest in testing
- Find the right tool for testing
- Share your experience in testing and help others
How to Mock Well in Tests
By Cheuk Ting Ho