brunogirin

brunogirin

Python Testing with pytest, Second Edition: explain the rationale behind the change in the fixture code (pages 143, 144)

In the “Testing at Multiple Layers to Avoid Mocking” section, the code for the cards_db fixture changes. In previous chapters, it was something like:

@pytest.fixture(scope="session")
def db(tmp_path_factory):
  """CardsDB object connected to a temporary database"""
  db_path = tmp_path_factory.mktemp('cards_db')
  db_ = cards.CardsDB(db_path)
  yield db_
  db_.close()

@pytest.fixture(scope="function")
def cards_db(db, request, faker):
  """CardsDB object that's empty"""
  db.delete_all()
  return db

When we reach this section, it changes to:

@pytest.fixture(scope="module")
def db_path(tmp_path_factory):
  db_path = tmp_path_factory.mktemp("cards_db")
  return db_path

@pytest.fixture()
def cards_db(db_path, monkeypatch):
  monkeypatch.setenv("CARDS_DB_DIR", str(db_path))
  db_ = cards.CardsDB(db_path)
  db_.delete_all()
  yield db_
  db_.close()

Explaning this code change could introduce some important testing concepts that junior developers may struggle with. By adding the monkeypatch.setenv line, you ensure that the high level API via the cards_db package will point to the same database as the one created manually via the cards.CardsDB class. This is necessary because your tests operate at two different levels at the same time. It might be useful to show it failing if you don’t set the environment variable and explain why.

This also introduces an important concept about how you design your tests. As mentioned elsewhere in the book, tests are meant to be independent and you need to ensure you know what they’re testing. One way this is often done is by designing tests such that the “When” part of the test is the only part that exercises the code under test (in this case, the cards_db package) while the “Given” and “Then” parts of the test are kept independent of the code under test. This way you know exactly what the “When” part is testing. However, when designing your tests that way, it means the “When” part is acting against a different layer from the “Given” and “Then” parts and you need to ensure those layers are configured the same.

This test design point introduces another point about making your app easily testable. In the case of the CardDB app, the fact that its main configuration option can be set via an environment variable is what makes this code possible.

Finally, this raises a point about test strategy. When I see code like this in my own projects, I immediately add a simple test suite to validate that the configuration options are properly handled. If those tests fail in the future, it is likely that a lot of other tests will fail in cascade. I typically set a special mark on those such as @pytest.mark.canary so if I suddently have a lot of tests failing, I check the canary ones first and make sure they are fixed as a priority on the basis that if those fail, a lot of others will fail too.

Apologies for such a complex and long-winded comment on this small change. When I started asking myself “why?” repeatedly when seeing that code change, I realised that there were a lot of logic behind that change that is natural to an experienced tester but not to a junior one. I don’t know if this would be better explained as an addition to this chapter, an aside, or any other form but I feel that it would be worthwhile to explain the rationale behind this code change.

First Post!

brianokken

brianokken

Author of Python Testing with pytest

Thanks for the feedback

Where Next?

Popular Pragmatic Bookshelf topics Top

jimmykiang
This test is broken right out of the box… — FAIL: TestAgent (7.82s) agent_test.go:77: Error Trace: agent_test.go:77 agent_test.go:...
New
brianokken
Many tasks_proj/tests directories exist in chapters 2, 3, 5 that have tests that use the custom markers smoke and get, which are not decl...
New
mikecargal
Title: Hands-on Rust: question about get_component (page 295) (feel free to respond. “You dug you’re own hole… good luck”) I have somet...
New
joepstender
The generated iex result below should list products instead of product for the metadata. (page 67) iex> product = %Product{} %Pento....
New
alanq
This isn’t directly about the book contents so maybe not the right forum…but in some of the code apps (e.g. turbo/06) it sends a TURBO_ST...
New
Chrichton
Dear Sophie. I tried to do the “Authorization” exercise and have two questions: When trying to plug in an email-service, I found the ...
New
leonW
I ran this command after installing the sample application: $ cards add do something --owner Brian And got a file not found error: Fil...
New
patoncrispy
I’m new to Rust and am using this book to learn more as well as to feed my interest in game dev. I’ve just finished the flappy dragon exa...
New
New
a.zampa
@mfazio23 I’m following the indications of the book and arriver ad chapter 10, but the app cannot be compiled due to an error in the Bas...
New

Other popular topics Top

PragmaticBookshelf
Rust is an exciting new programming language combining the power of C with memory safety, fearless concurrency, and productivity boosters...
New
mafinar
This is going to be a long an frequently posted thread. While talking to a friend of mine who has taken data structure and algorithm cou...
New
AstonJ
If you get Can't find emacs in your PATH when trying to install Doom Emacs on your Mac you… just… need to install Emacs first! :lol: bre...
New
First poster: joeb
The File System Access API with Origin Private File System. WebKit supports new API that makes it possible for web apps to create, open,...
New
PragmaticBookshelf
Author Spotlight Jamis Buck @jamis This month, we have the pleasure of spotlighting author Jamis Buck, who has written Mazes for Prog...
New
New
hilfordjames
There appears to have been an update that has changed the terminology for what has previously been known as the Taskbar Overflow - this h...
New
sir.laksmana_wenk
I’m able to do the “artistic” part of game-development; character designing/modeling, music, environment modeling, etc. However, I don’t...
New
Fl4m3Ph03n1x
Background Lately I am in a quest to find a good quality TTS ai generation tool to run locally in order to create audio for some videos I...
New
mindriot
Ok, well here are some thoughts and opinions on some of the ergonomic keyboards I have, I guess like mini review of each that I use enoug...
New

Sub Categories: