Automated Testing in Python with pytest, tox, and GitHub Actions

    7
    39



    Automated testing in Python is an important way to take your Python project to the next level of dependability and professionalism. There are a lot of steps, but it’s not too difficult to setup your tests to run automatically across multiple different operating systems and versions of Python whenever you push a commit or receive a pull request on your repository. We show you the entire process, step by step, to take a plain old project and turn it into an installable package with automated tests that run when you push to GitHub. We use pytest, mypy, and flake8 for testing, type checking, and code linting. Then we use tox to run all of these commands in isolated virtual environments. Finally, we use GitHub actions to run tox on a push or a pull request. Automated testing is the first and most important step in the more general world of continuous integration (CI), and continuous delivery or continuous deployment (CD). In this video we focus on automated testing, not on general CI/CD, as there is already PLENTY of material to cover.

    ― mCoding with James Murphy (https://mcoding.io)

    Source code: https://github.com/mCodingLLC/SlapThatLikeButton-TestingStarterProject
    Other code: https://github.com/mCodingLLC/VideosSampleCode
    pytest docs: https://docs.pytest.org/en/latest/
    mypy docs: https://mypy.readthedocs.io/en/stable/index.html
    flake8 docs: https://flake8.pycqa.org/en/latest/
    setuptools (setup py and setup cfg) docs: https://setuptools.readthedocs.io/en/latest/index.html
    tox docs: https://tox.readthedocs.io/en/latest/
    GitHub Actions docs: https://docs.github.com/en/actions
    tox-gh-actions repo: https://github.com/ymyzk/tox-gh-actions

    SUPPORT ME ⭐
    —————————————————
    Sign up on Patreon to get your donor role and early access to videos!
    https://patreon.com/mCoding

    Feeling generous but don’t have a Patreon? Donate via PayPal! (No sign up needed.)
    https://www.paypal.com/donate/?hosted_button_id=VJY5SLZ8BJHEE

    Want to donate crypto? Check out the rest of my supported donations on my website!
    https://mcoding.io/donate

    Top patrons and donors: Laura M, Jameson, John Martin, Dragos C, Vahnekie, Pieter G, Sigmanificient, Casey G

    BE ACTIVE IN MY COMMUNITY 😄
    —————————————————
    Discord: https://discord.gg/Ye9yJtZQuN
    Github: https://github.com/mCodingLLC/
    Reddit: https://www.reddit.com/r/mCoding/
    Facebook: https://www.facebook.com/james.mcoding

    CHAPTERS
    —————————————————
    0:00 Intro
    1:21 Overview of the video
    2:56 Structuring your project
    8:35 Pytest, mypy, flake8
    10:58 Pytest features
    18:34 Test multiple envs with tox
    21:18 Test on commit with GH Actions
    25:22 Readme badge
    26:34 Thanks

    source

    Previous articleComplete Master Class on AWS S3 from 0 to Hero python Boto3
    Next articleBig Data Analytics Lectures | Introduction to Hadoop in Hindi| Part 1

    39 COMMENTS

    1. This is one of the best and most complete python test setup guide I have ever seen in text or video form. While I knew about all the things mentioned here, your clear and incremental explanation has definitely helped me refresh everything and their interconnections. I am going to point every newbie (to testing) to this video hereafter. Thank you.

    2. I have a question regarding the project structure and imports. I have used this folder layout for a few projects now and never had any issues. With one project I set up yesterday I now have the issue that python refuses to import via "from package.subpackage import foo". It works if i use "from src.package.subpackage import foo", though. I can't remember messing with pythonpath or anything – I just set up the src folder as content root in pycharm, as always. Can anyone give me some pointers what may have changed or what I did wrong?

    3. Hi, can't you merge almost all your configuration files into a single pyproject.toml? I believe, tox.ini, setup.cfg can be integrated into sections in the TOML file. As development is mainly associated with testing I suggest to move the development dependencies into the tests/ directory as a new requirements.txt file. This should also ensure that services looking for dependency files (and only matching for requirements.txt) will find that second set of dependencies (require.io / libraries.io / GitHub dependencies / …). I also suggest to reference your main dependency file with -r ../requirements.txt, so when a CI server or developer installs the development dependencies with pip install -r tests/requirements.txt, he gets all what's needed.

      Further you have duplications in your setup.cfg by redefining your requirements for setup.py. You can read the requirements.txt file in setup.py and hand it over to setup.py. There is actually no need to have a static setup.cfg of you combine it with a dynamic setup.py. Either you completely go static (which I don't recommend) or dynamic like reading README.md, requirements.txt etc. from other sources (single source of truth).

      Maybe you want to have a look at pyTooling (https://github.com/pyTooling). pyTooling/pyTooling provides functions and helpers for setuptools to read and extract all needed information for existing sourcefiles. You might also have a look at pyTooling/Actions, which provides GH Action job templates to create complex GHA workflows including packaging, pytest, mypy, Sphinx, …

      If you have questions or want to discuss more details, you can find me on Gitter as "Paebbels".

    4. Project name: "Slap that like button" *with a characteristic look at the viewer LOL.

      You have really good oration. And good video, having recently implemented a lot of CI/CD stuff including this within the company I work for, it is great to see great tutorials on how to do this for others.

      Also, the great thing about fixtures is you can also return functions and classes and pass the parameters into that function inside the fixture by calling the fixture like fixture_name(args for the inner function of the fixture) or with a class you can also do fixture_name(args).method

    5. Really awesome video! Great great job. The only thing I think would be nice to add is that is a good practice to check your test fails first, then make it work. Moreover few words about the test-driven development would've been good, but I understand would've opened a way bigger topic.

    6. Thank you for the content! Just one thing: Not sure if it's best practice to list specific versions under requirements.txt, as it very likely creates conflicts with other packages that follow the same practice. I think it's best to list a lower and an upper bound since breaking changes mostly occur in-between major version changes.

    7. What is the best way to organize the test cases for each function? for example in case I have a main.py with 3 functions, should I have 1 test_main.py file with different test cases for each function? and if that is the case, should I group the test cases specific for each function? since I saw in the video that you separated the test cases based on the evaluation type for 1 function, but what about the others? or does not matter since all the test cases are in the same module?.

    8. Somehow I get an error.

      I downloaded the repo and opened it in PyCharm. Then I typed "pip install -e ."
      PIP tells me that slapping was installed successfully with "Successfully installed slapping-0.0.0". But still, PyCharm marks the import of slapping in test_slapping.py as "Unresolved reference".

      Also after running "pytest" as well as "pytest src" it gives me this error message:

      ERROR: usage: pytest [options] [file_or_dir] [file_or_dir] […]
      pytest: error: unrecognized arguments: –cov=slapping

      Any suggestions on what I'm doing wrong?

      I didn't change or alter any file. Just followed the tutorial using the terminal.

    9. Hey people, I was trying to do something like what James is going, but I ran into some problems. Basically, in order for my package to set up, I need to specify the path to some static C++ libraries. My problem is that, when I run tox, it basically copies all that is in my package, except the compiled libraries and so, when it is time to set up, it does not find them and it raises some errors.

      Is there a way to specify where the binaries are, whether it is the setup.py file or else?