quarantine diaries weekend 7
(written for weekend of April 25, 2020)
tests refresher and mocking aws services
joined a new team and reviewing sample tests that have been written for our repos. reviewing the various ways that we can mock aws services in our tests.
we are using pytest.
to use pytest:
pip install pytest
most basic setup for tests:
- in your repository add a
tests
folder - add
__init__.py
file - add a test file prefixed with
test_
with your test, ie:
# content of test_sample.py
def func(x):
return x + 1def test_answer():
assert func(3) == 4
- run
pytest
python moto
library
creates full blank environment mocking aws services, calls to services are mocked out. for example you can create a bucket for testing of code that upload a file, for testing you can fetch the same contents of the file.
for aws secrets manager, believe we can only add secrets via the cli or console. so for my test of fetch secrets, i will mock the fetch command injecting a sample response that would be expected from secrets manager, then assert that the config parser to parse in format expected.
very important to note, even though in documentation, using the decorator does not seem to require `mock.start()` you do still need this whether in your fixture or in your test method.
pytest fixtures
functions that run before each test function that uses it. used to feed some input data to a test, ie. database connections, urls. avoids running the same code to get an expected response for every test that uses it, attach fixture to these tests instead
use the decorator before function:
@pytest.fixture
make sure it has a return value, indicate the function/fixture name as one of the test method’s parameters. the fixture function will get run and feed the fixture’s return value as an input param you can use in the test method.
pytest monkeypatch
also helps with invoking a response that can’t be easily tested ie. network access. docs provide several options:
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)
when mocking a class and method(s), use @staticmethod so that you can call the method without having to instantiate the class first.
botocore.stub
according to docs — this allows you to stub out requests so you don’t have to hit an endpoint to write tests. this requires detailing out the expected request and responses for each method you are stubbing.
this did not end up working for me, it seems to still make network calls. maybe i’m misunderstanding what this library is doing for us.
pytest-mock
useful for mocking module functions and class methods. once i got this working it was the most easy and cleanest way to mock functions and methods.
to use, run
pytest-mock
for example, for a requests post method instead of using monkeypatch to mock entire classes and method returns, you can do this instead:
def test_submit_some_event(mocker):
class MockResponse:
@staticmethod
def json():
return MOCK_POST_RESPONSE
mocker.patch.object(requests, 'post')
requests.post.return_value = MockResponse()
submit_some_event([[{'id':'1'}]])
requests.post.assert_called_once()
for checking your test coverage you can run
pip install pytest-cov
if i try running coverage at top repo level
pytest --cov <repo/top folder name> tests
i getting error:
overage.py warning: Module <repo/top folder name> was never imported. (module-not-imported)
Coverage.py warning: No data was collected. (no-data-collected)
WARNING: Failed to generate report: No data to report.
i can get coverage when i run at module level instead:
pytest --cov <module under repo.top folder name> tests/test_lambda_function.py
to see detailed view of code coverage in html format html arg:
pytest --cov <module under repo/top folder name> tests/test_lambda_function.py --cov-report=html
if you are using environment variables in the tested method, you can hardcode values at beginning of your test script:
os.environ['environment'] = 'dev'os.environ['region'] = 'us-east-1'