How to write parametrized tests in Python with pytest ๐ŸŽฅ

Hi ๐Ÿ‘‹

Welcome to another video tutorial on how to write parametrized tests in Python using pytest.

If you want to follow along, here’s the code that I’ve tested in the video.

from typing import List


class Solution:
    def move_zeroes(self, nums: List[int]) -> None:
        last_zero = 0
        index = 0
        while index < len(nums):
            if nums[index] != 0:
                nums[last_zero], nums[index] = nums[index], nums[last_zero]
                last_zero += 1
            index += 1


def main():
    solution = Solution()
    arr = [1,0,1]
    solution.move_zeroes(arr)
    print(arr)


if __name__ == '__main__':
    main()

Thanks for watching! ๐Ÿ˜„

Testing Python projects with Tox

Hi ๐Ÿ‘‹

In this article I will show you how to test your Python projects with Tox.

Introduction

Tox is a tool for automating testing in Python, their vision is to standardize the testing process. It can be used to easily test your project using multiple Python interpreters and run various commands.

Getting Started

To get started all you need to add to your project is a tox.ini file. To simplify running the tests we will make use of the following Dockerfile, which contains Python interpreters for 3.6 and 3.7

FROM ubuntu:20.04

RUN apt update && apt install -y software-properties-common \
               && add-apt-repository ppa:deadsnakes/ppa \
               && apt install -y python3.6 && apt install -y python3.7 \
               && apt install -y python3-pip && pip3 install tox

VOLUME /code

WORKDIR /code
ENTRYPOINT tox

A tox.ini file which tests using python 3.6 and python 3.7 looks like this:

# content of: tox.ini , put in same dir as setup.py
[tox]
skip_missing_interpreters = True
envlist = py36,py37

[testenv]
# install pytest in the virtualenv where commands will be executed
deps =
    pytest==6.2.1
    pytest-cov==2.11.1
    responses==0.13.3
commands =
    # NOTE: you can run any command line tool here โ€“ not just tests
    pytest

[testenv:bamboo]
commands =
  pytest โ€”junitxml=results.xml \
    โ€”cov=your-moduleโ€”cov-config=tox.ini โ€”cov-report=xml
    coverage2clover -i coverage.xml -o clover.xml
deps =
    {[testenv]deps}
    coverage2clover

We have two environments: testenv and testenv:bamboo, the later one being used for coverage reporting in Bamboo using clover. To run Tox with a specific environment you’d type tox -e bamboo.

To run the test via the Dockerfile, first you’d build the docker container using: docker build . -f Dockerfile -t tox

Then, you’d run the container with docker run -v “$(pwd)”:”/code” tox -e bamboo to test with the Bamboo environment or just docker run -v “$(pwd)”:”/code” tox for the default env.

Practical Example

Here’s an example that you can use to follow along. We have the following files:

@denis โžœ tox_article ls
__pycache__  tests.py  tox.ini
# @denis โžœ tox_article cat tests.py
import unittest

class TestStringMethods(unittest.TestCase):

    def test_upper(self):
        self.assertEqual('foo'.upper(), 'FOO')

    def test_isupper(self):
        self.assertTrue('FOO'.isupper())
        self.assertFalse('Foo'.isupper())

    def test_split(self):
        s = 'hello world'
        self.assertEqual(s.split(), ['hello', 'world'])
        # check that s.split fails when the separator is not a string
        with self.assertRaises(TypeError):
            s.split(2)

if __name__ == '__main__':
    unittest.main()%
# @denis โžœ tox_article cat tox.ini
[tox]
skip_missing_interpreters = True
envlist = py36,py37
skipsdist = True

[testenv]
commands =
    python -m unittest%

Running Tox in our docker image will yield the following output:

@denis โžœ tox_article docker run -v "$(pwd)":"/code" tox
py36 create: /code/.tox/py36
py36 run-test-pre: PYTHONHASHSEED='520882151'
py36 run-test: commands[0] | python -m unittest
...
----------------------------------------------------------------------
Ran 3 tests in 0.000s

OK
py37 create: /code/.tox/py37
py37 run-test-pre: PYTHONHASHSEED='520882151'
py37 run-test: commands[0] | python -m unittest
...
----------------------------------------------------------------------
Ran 3 tests in 0.000s

OK
___________________________________ summary ____________________________________
  py36: commands succeeded
  py37: commands succeeded
  congratulations ๐Ÿ™‚

The same tests are run twice, first with Python 3.6 and then with Python 3.7.

Thanks for reading and happy testing! ๐Ÿ”ง