Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
November 14, 2022 01:26 am GMT

Unit Testing Python Code With The unittest Framework

Unit testing is an important part of software development as it isolates different components of a software program or system and checks if it operates the right way. It ensures that the code meets quality standards and that flaws or bugs in a system can be properly traced back to the isolated unit of code that is failing, and remedy the failure promptly.

My static site generator - rwar is evolving and getting better over time, which means I need to ensure that the code is professional and of high quality! Also, being able to work with testing frameworks and learning them well can help you stand out during interviews.

The learning curve

I was not used to writing tests much for the personal projects and small group projects I have worked on so far, which is why thinking of code from a test-driven perspective was challenging for me. I could feel my neurons firing as I was trying hard to think how to put all the unit testing pieces together. Until now, I only had a little bit of experience working with the Jest testing framework for JavaScript, and Junit for Java, so I had to go through a big learning curve to understand the unittest framework. This explanation was really useful for me to get an overview of what unittest is and how to get started with it. I used to underestimate the importance of reading documentation, but I know now how crucial that skill is.

Why unittest framework

Upon doing some research on some of the most popular testing frameworks for Python I came across pytest, nose, and unittest. They are all great tools, however, I decided to give unittest a try for this project because unittest was inspired by JUnit which I had some prior experience with. It is always good to experiment and learn different frameworks to eventually find out what we like most. I am going to give pytest a try for my next Python project. Each testing framework has its conventions for naming files and structuring code, which is another important reason to look at examples and go through the documentation for the framework of your choice.

Having a good test plan

It is really important to have a good test plan before starting.

1) Understanding what your code does

  • What kind of inputs the function takes
  • What are the program's functions
  • What are the expected outputs

2) Good and bad test scenarios

  • Think of the success cases and cases that are anomalies. For example, if you are building a school management system - adding the same student twice should result in some sort of warning.

3) What does not need testing

  • A good example of what does not need testing would be functions from external packages. Another example would be a program invoking functions where the code was already tested.

4) Writing the tests

  • Then, comes the test writing part!
  • For example, if you are building a school management system - then you can test for functions such as adding students, loading an existing student, adding the same student twice, and so on.

The process

For my project I have created two test files - test_parser.py and test_ssg.py - under the test folder.

As stated in this documentation:

unittest requires that:

  • You put your tests into classes as methods
  • You use a series of special assertion methods in the unittest.TestCase class instead of the built-in assert statement

1) To get started with unittest I first had to:

import unittest

2) Then, I created a class SSGTest that inherits from the TestCase class and it is meant to test my class SSG

class SSGTest(unittest.TestCase):

Similarly, I have done this for my other test_parser.py file.

class CLIParser(unittest.TestCase):

An example:

It is hard to go over all the test I have written for my project because there are many! However,
here is an example from test_parser.py. Notice, how I have used descriptive names for my test. Here I am checking if the test has any input files or not.

class CLIParser(unittest.TestCase):def test_without_input(self):with self.assertRaises(SystemExit) as err:get_parser_args([])self.assertEqual(err.exception.code, 2, "No input directory provided")

assertRaises() verifies that a specific exception gets raised.

with self.assertRaises(SomeException):    do_something()

assertEqual() checks for an expected result.

with self.assertRaises(SomeException) as cm:    do_something()the_exception = cm.exceptionself.assertEqual(the_exception.error_code, 3)

When to use setUp() and tearDown()

If you go through my code on test_ssg.py, you will see that I have used the setUp() and tearDown() methods. For every test in test_ssg.py, a temporary file was created which needed to be deleted after the test, which is what I could accomplish with setUp() and tearDown(). These were repetitive processes that I could simplify using these methods. Imagine you have a suite with 10 tests and 7 of them require the same setup/teardown code, while the 3 don't, setup and teardown gives you a nice way to refactor the code. It is often used when we need to create a fake database for testing purposes or for mocking purposes.

Here is an example of how I have used it in my code:

class SSGTest(unittest.TestCase):def setUp(self):self.tempdir = tempfile.TemporaryDirectory()self.output = self.tempdir.nameself.input = os.path.join(self.output, "input")os.mkdir(self.input)self.stylesheet = "test-stylesheet-link"def tearDown(self):self.tempdir.cleanup()

Test discovery

Test discovery is the process of locating where the tests are in your codebase. This means that you don't have to explicitly state where the tests are located as the testing framework can automatically locate them based on the matching patterns of the naming convention. In this case, it will look for test*.py `files.

As stated in the documentation:

Unittest supports simple test discovery. To be compatible with test discovery, all of the test files must be modules or packages importable from the top-level directory of the project (this means that their filenames must be valid identifiers).
Test discovery is implemented in TestLoader.discover(), but can also be used from the command line. The basic command-line usage is:


cd project_directory
python -m unittest discover

Upon running that command, it shows the number of passing and failing tests. I had two tests that were failing, so I went back and fixed those before pushing my changes to GitHub.

Documentation

A good software or system is properly documented. I used to underestimate the value of documentation until I participated in Hacktoberfest and had to work on large open-source projects. Some interesting projects I wanted to contribute to had unclear documentation and even though I wanted to work on them, I just couldn't. The projects I ended up contributing to were all properly structured and had clear documentation.

The same goes for rwar. I am trying to write clear documentation so that anyone interested in using it or contributing to it will find it easy to do so. I have added the instructions for testing on the CONTRIBUTING.md file!

For the full codebase, please feel free to check it out on GitHub :)


Original Link: https://dev.to/saminarp/unit-testing-python-code-with-the-unittest-framework-21d8

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To