To follow this tutorial, you would need to install 5 and 6. I provided commands for both python library installation options: pip and poetry. Show If you are using pip for installation (default):
If you are using poetry for installation:
2. What is Unit Testing and Test Pyramid?Unit Testing is a software testing method to test the smallest piece of code that can be isolated, i.e. a function (method). The concept of Test Pyramid as shown below, is introduced by Mike Cohn in his book Succeeding with Agile. It defines the testing strategy with a great visualization, covering the three major types of tests: unit tests, integration tests and end-to-end tests, and the proportion of each out of the entire testing strategy. According to the pyramid, when it is at the bottom it normally means more isolated and faster tests (in other words: cheaper); while moving up the pyramid, it means more integrated and slower tests (in other words: expensive). At the tip of the pyramid could means manual testing. Thus, we should cover code with more automated unit tests and less manual tests. Test Pyramid 3. Why do we need Unit Testing?People would tend to rush the requested feature delivering but ignoring the importance of writing test cases. The ignorance could be due to they see it as a waste of time and not seeing the damage it could cause. However, let me tell you the benefits that writing Unit tests will brings you:
4. What makes a good unit test then?Below are the six qualities that will make a good unit test. How we could achieve the first four qualities are covered in the sections below. Independent The testing is focusing on the function itself and NOT on all the dependencies, which include API call, DB connections, other functions from your application or third party libraries. All dependencies should be properly mocked. Multiple assertions are allowed only if they are all testing one feature. When a test fails, it should pinpoint the location of the problem. Tests should be isolated — not rely on each other. We shouldn’t make assumptions about the order of test execution. Fast A good unit test’s response time should be less than a second. This is also a good and straightforward way to evaluate its independency. Any unit test taking longer than that should be questioned in terms of its dependency. And maybe that’s no longer unit test but integration test. Repeatable The tests should produce the same output no matter when and how many time it runs. Readable (Consistency) The test case serves part of the function documentation and it’s frequently read during debugging processes. Thus, it’s important to be understood easily. Try to stick to one naming convention for naming test file, test case, sequence of assertion ( 7) and the way of writing mocks.Automatic The process of running the unit tests should be integrated into CI/CD tool such as Jenkins, so the code quality can be continuously ensured with every new change. This is not explained in this article. Feel free to read more about Jenkins, GitHub Actions or any CI/CD tools. Thorough (Coverage) As mentioned in the Test Pyramid, we should get more unit tests as they are cheaper and faster. Coverage is the key metric to evaluate the degree of which source code has been tested. Any uncovered lines could result to a corner case bug one day with more expensive identification and resolving process. Here is the formula to calculate code coverage, which is also called line coverage. This is mostly used.
If you ask me, what is the ideal code coverage that we should meet. There is no such thing that works for every product. I would recommend to first reach 80% and make sure the coverage can be maintained with every single change, then continuously work on improving the code coverage towards 90%. 5. How to write Unit Test with PyTest (Basics)?Here are some basics to help you get started with PyTest. Below is a typical folder structure for a Python project/application, where you have a 8 folder that is outside of your 9 folder.
5.1 ParametrizingHere, let me introduce the pytest parametrizing decorator, which checks whether multiple inputs lead to expected output. It’s a little bit similar as looping through each of the 3 input set (a+b), however, the testing result will let you know whether each of them has passed. But writing a loop of asserting each of them would stop at the middle once it failed. This example tests when a=10, b=5, whether expected is 15; and similar for the other two cases.
And here, we try to cover different scenarios of adding two positives, two negatives or one positive and one negative numbers. You may also add floating points test cases (which usually should be considered for testing division). We have covered how to write a test case with pytest and parametrizing. Let’s look at another concept in PyTest, which is fixture. 5.2 FixtureFixture is a function with decorator that creates a resource and returns it. If you need the same test input for multiple test cases, you can use fixture to prepare the arrange step, just as the example below. This reduces code duplications.
6. How to mock dependencies properly in various scenariosWe have learnt what makes a good Unit Test in the previous sections. And mocking is exactly the technique can help us writing independent, fast, repeatable test cases. Mocking can be used to isolate and focus attention on the code under test rather than the behavior or state of external dependencies. Mocking replaces dependencies with carefully managed replacement objects that mimic the real ones’ behavior. Let’s start with a simple mocking example. Example 1:We have a function sleep for couple of seconds. Let’s assume we have some other processing steps after the sleep.
And here is the test case. 0
Example 2:We have function 0 in 1 as following to tell us what time of day it is now. It will return us the string of Night/Morning/Afternoon/Evening, depending on the hour range. 1How would you test this? If we just define a test case at 10 am, it will not be valid in the afternoon. The test result would highly depend on when you run the test. And the test case would be not repeatable no matter when you write it. Therefore, we needs to fix the time whenever the test cases runs. And let’s see how we do this with mocking.
Now, you have learnt about the ways we use to mock third-party libraries. Let’s look at some other examples with different scenarios. More Examples
7. Key TakeawaysIn this blog post, you’ve learnt the importance of unit testing, how to use PyTest with parametrizing & fixture and mocking techniques for different scenarios. |