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

jimschubert
In Chapter 3, the source for index introduces Config on page 31, followed by more code including tests; Config isn’t introduced until pag...
New
ianwillie
Hello Brian, I have some problems with running the code in your book. I like the style of the book very much and I have learnt a lot as...
New
jamis
The following is cross-posted from the original Ray Tracer Challenge forum, from a post by garfieldnate. I’m cross-posting it so that the...
New
JohnS
I can’t setup the Rails source code. This happens in a working directory containing multiple (postgres) Rails apps. With: ruby-3.0.0 s...
New
joepstender
The generated iex result below should list products instead of product for the metadata. (page 67) iex> product = %Product{} %Pento....
New
New
hazardco
On page 78 the following code appears: <%= link_to ‘Destroy’, product, class: ‘hover:underline’, method: :delete, data: { confirm...
New
akraut
The markup used to display the uploaded image results in a Phoenix.LiveView.HTMLTokenizer.ParseError error. lib/pento_web/live/product_l...
New
jonmac
The allprojects block listed on page 245 produces the following error when syncing gradle: “org.gradle.api.GradleScriptException: A prob...
New
New

Other popular topics Top

PragmaticBookshelf
Stop developing web apps with yesterday’s tools. Today, developers are increasingly adopting Clojure as a web-development platform. See f...
New
AstonJ
Or looking forward to? :nerd_face:
498 14002 274
New
brentjanderson
Bought the Moonlander mechanical keyboard. Cherry Brown MX switches. Arms and wrists have been hurting enough that it’s time I did someth...
New
PragmaticBookshelf
Create efficient, elegant software tests in pytest, Python's most powerful testing framework. Brian Okken @brianokken Edited by Kat...
New
PragmaticBookshelf
Use WebRTC to build web applications that stream media and data in real time directly from one user to another, all in the browser. ...
New
foxtrottwist
A few weeks ago I started using Warp a terminal written in rust. Though in it’s current state of development there are a few caveats (tab...
New
AstonJ
We’ve talked about his book briefly here but it is quickly becoming obsolete - so he’s decided to create a series of 7 podcasts, the firs...
New
Help
I am trying to crate a game for the Nintendo switch, I wanted to use Java as I am comfortable with that programming language. Can you use...
New
CommunityNews
A Brief Review of the Minisforum V3 AMD Tablet. Update: I have created an awesome-minisforum-v3 GitHub repository to list information fo...
New
PragmaticBookshelf
Explore the power of Ash Framework by modeling and building the domain for a real-world web application. Rebecca Le @sevenseacat and ...
New

Sub Categories: