Adding --baseurl Argument to My Website Pytests

Posted by Ryan Himmelwright on Sun, Nov 6, 2022
Tags website, hugo, python, testing
Sarah P. Duke Gardens, Durham NC

When I run the pytests for my website, they run against whatever hugo server I have running. While writing a post, this is usually the same machine I’m writing from. When I commit changes, the CI pipeline automatically runs the tests on my gitlab CI runner node. The IP address of the server to test is determined by a BASE_URL constant variable defined in my constants.py file.

Changing the variable in this file, and not accidentally pushing it in git is super annoying. So, I finally sat down one night and moved this variable out of the constants file, converting it to an optional pytest argument.

Steps

While this might not the best approach, it was easy enough to implement in a very short period of time, and seems to be working great so far. Here are the steps I took to convert my tests from using a constant variable to the optional argument.

  • Add pytest_addoption() function to the conftest.py file
def pytest_addoption(parser):
    parser.addoption(
        "--base-url", action="store", default="http://ci-runner.website.net"
    )

First, I added a pytest_addoption() function definition to my conftest.py file. This function allows me to add command line options to pytest. I added a single option for now, --base-url, with it’s action set to "store". Additionally, I set the default value to the address of my CI runner node. This way, if I don’t provide a value it defaults to using my CI runner.

  • Add base_url fixture to the conftest.py file
@pytest.fixture()
def base_url(request):
    return request.config.getoption("--base-url")

Next, I added a new fixture (base_url) to replace my old constant variable (BASE_URL) with. This fixture simply returns the value of the --base-url option just added.

  • Update other fixtures to reference the base_url fixture
## Example Before
@pytest.fixture(params=POST_NAMES)
def post_url(request):
    """Returns the post urls for testing."""
    return BASE_URL + "/post/" + request.param.lower()

## Example After
@pytest.fixture(params=POST_NAMES)
def post_url(request, base_url):
    """Returns the post urls for testing."""
    return base_url + "/post/" + request.param.lower()

Next, I had to update any other fixtures in the conftest.py file to use the base_url fixture, instead of the old constant variable. I just added base_url to the parameter list, and replaced the variable name in the fixture definitions.

  • Update test functions to use base_url fixture
## Example Before
@flaky
def test_md_links(post_md_link):
    """Checks that the markdown links are not broken."""
    if post_md_link in SKIP_LINKS:
        pytest.skip(f"{post_md_link} in skip list")
    elif post_md_link.startswith("http") or post_md_link.startswith("https"):
        url = post_md_link
    else:
        url = BASE_URL + post_md_link.lower()
    response = requests.get(url)
    assert response.status_code != 404, f"The link {post_md_link} is not found."
    assert response.status_code != 403, f"The link {post_md_link} is forbidden."

## Example After
@flaky
def test_md_links(base_url, post_md_link):
    """Checks that the markdown links are not broken."""
    if post_md_link in SKIP_LINKS:
        pytest.skip(f"{post_md_link} in skip list")
    elif post_md_link.startswith("http") or post_md_link.startswith("https"):
        url = post_md_link
    else:
        url = base_url + post_md_link.lower()
    response = requests.get(url)
    assert response.status_code != 404, f"The link {post_md_link} is not found."
    assert response.status_code != 403, f"The link {post_md_link} is forbidden."

Just like the fixtures, I had to update the test functions to use the base_url fixture, instead of the BASE_URL constant. Again, this was done by adding the fixture name as a parameter to the test function, and swapping out the references in the code.

  • Remove BASE_URL from constants.py, and imports of it in other files.

Lastly, I did some cleanup and removed the BASE_URL definition from the constants.py file, along with any imports of that variable in the other files.

Results

With those changes, what are the results? Well, I can still run my test execution command the same as before, and it will default to running the tests against my CI server:

pipenv run pytest --workers 10 -v .

However, if I want to point the tests at a hugo server different from the default, I can now define it with the --base-url flag:

pipenv run pytest --workers 10 -v --base-url "http://10.0.9.104:1313" .

Conclusion

The best part of this change is that I don’t have to worry about forgetting to switch that config back and forth when pushing to my repo. I simply set the variable when running the tests. Done. I should have made this change years ago.

Next Post:
Prev Post:

Using gTile Custom Resolutions in Gnome Wayland