Pytest Tutorial
Strange phenomenon of print in Pytest framework
test_02_01.py
""" pytest middle print usage of """ from assertpy import assert_that import pytest # fun1 is the function we test: pass a parameter automatically +1 def fun1(x): """ If we accidentally write 1 to 0 when we develop.1 :param x: :return: """ return x + 0.1 def test_fun1(): print('This is the failing use case,this print will print') assert_that(fun1(10)).is_equal_to(11) def test_fun2(): print('This is a successful use case,this print won't print') assert_that(fun1(10)).is_instance_of(float)
We execute in the terminal
pytest .\test_02_01.py
Output result:
PS D:\code\pytest_test\test_2_day> pytest .\test_02_01.py ============================================================ test session starts print('This is the failing use case,this print will print') > assert_that(fun1(10)).is_equal_to(11) E AssertionError: Expected <10.1> to be equal to <11>, but was not. test_02_01.py:20: AssertionError ----------------------------------------------------------- Captured stdout call ----------------------------------------- This is the failing use case,this print will print ========================================================== short test summary info ======================================= FAILED test_02_01.py::test_fun1 - AssertionError: Expected <10.1> to be equal to <11>, but was not. ======================================================== 1 failed, 1 passed in 0.14s ===================================== PS D:\code\pytest_test\test_2_day>
You can see ---- Captured stdout call --- The following is a failed use case, this print will print, but this is a successful use case, this print will not print
Here we need to note that test_fun1 is a failed use case and test_fun2 is a successful use case. So to summarize:
like,The execution result of the use case is Pass,in that use case print won't print like,The execution result of the use case is Fail,in that use case print will print out
What if I just want to print out all the prints?
We execute in the terminal:
pytest .\test_02_01.py -s
Output result:
PS D:\code\pytest_test\test_2_day> pytest .\test_02_01.py -s ============================================================ test session starts ============================================== platform win32 -- Python 3.10.5, pytest-7.1.2, pluggy-1.0.0 rootdir: D:\code\pytest_test\test_2_day collected 2 items test_02_01.py This is the failing use case,this print will print F This is a successful use case,this print won't print . ================================================================= FAILURES =================================================== _________________________________________________________________ test_fun1 ____________________________________________________ def test_fun1(): print('This is the failing use case,this print will print') > assert_that(fun1(10)).is_equal_to(11) E AssertionError: Expected <10.1> to be equal to <11>, but was not. test_02_01.py:20: AssertionError ========================================================== short test summary info ============================================ FAILED test_02_01.py::test_fun1 - AssertionError: Expected <10.1> to be equal to <11>, but was not. ======================================================== 1 failed, 1 passed in 0.11s ========================================== PS D:\code\pytest_test\test_2_day>
In this way, our prints are all printed, which is ugly~
Next we introduce the log~
no no no~
Loguru third-party log library
We don't need the logging module that comes with python, we are introducing a third-party library today: Loguru
Loguru is a library which aims to bring enjoyable logging in Python.
install logurn
pip install loguru
This time we first create a mytest_02_02.py to eliminate the pytest framework to automatically capture the beginning of the test file.
""" loguru use """ from assertpy import assert_that from loguru import logger import pytest logger.debug('This is my first time using loguru')
Output result:
2022-07-06 23:12:35.809 | DEBUG | __main__:<module>:7 - This is my first time using loguru
I fell asleep, continue tomorrow
Today we continue the loguru....
The printed log is saved to a file
Create a mytest_02_03.py
from loguru import logger logger.add("mytest.log", rotation="500 MB") # In this way, a mytest.log file is created. If the file is larger than 500m, it will be re-saved, and the following log information is stored in the file. # The following functions are self-understood logger.add("file_2.log", rotation="12:00") # New file is created each day at noon logger.add("file_3.log", rotation="1 week") # Once the file is too old, it's rotated logger.add("file_X.log", retention="10 days") # Cleanup after some time logger.add("file_Y.log", compression="zip") # Save some loved space
Continue to modify mytest_02_03.py
from loguru import logger logger.add("mytest.log", rotation="500 MB") # In this way, a mytest.log file is created. If the file is larger than 500m, it will be re-saved, and the following log information is stored in the file. logger.info('This is a info') # file storage terminal will print logger.debug('This is a debug') logger.warning('This is a warning') logger.error('This is a error')
continue:
Loguru favors the much more elegant and powerful {}formatting over%, logging functions are actually equivalent to str.format().
# loguru supports automatic string formatting logger.info("If you're using Python {}, prefer {feature} of course!", 3.6, feature="f-strings") # print must add format print("If you're using Python {}, prefer {feature} of course!".format(3.6, feature="f-strings"))
When our project gets bigger and bigger, our py file has many opportunities, and a common log module will be called. If something goes wrong, we need to know which file\function to call: @logger.catch
Continue to modify mytest_02_03.py
from loguru import logger logger.add("mytest.log", rotation="500 MB") # In this way, a mytest.log file is created. If the file is larger than 500m, it will be re-saved, and the following log information is stored in the file. @logger.catch def my_function(x, y, z): # An error? It's caught anyway! return 1 / (x + y + z) my_function(0, 0, 0) # Passing parameters in this way will definitely report an error, let's take a look at the output
Console output:
2022-07-07 23:30:07.902 | ERROR | __main__:<module>:16 - An error has been caught in function '<module>', process 'MainProcess' (16084), thread 'MainThread' (15688): Traceback (most recent call last): > File "D:\code\pytest_test\test_2_day\my_test_02_02.py", line 16, in <module> my_function(0, 0, 0) └ <function my_function at 0x0000019D1CCE4310> File "D:\code\pytest_test\test_2_day\my_test_02_02.py", line 13, in my_function return 1 / (x + y + z) │ │ └ 0 │ └ 0 └ 0 ZeroDivisionError: division by zero Process finished with exit code 0
The result output in the mytest.log file:
2022-07-07 23:30:07.902 | ERROR | __main__:<module>:16 - An error has been caught in function '<module>', process 'MainProcess' (16084), thread 'MainThread' (15688): Traceback (most recent call last): > File "D:\code\pytest_test\test_2_day\my_test_02_02.py", line 16, in <module> my_function(0, 0, 0) └ <function my_function at 0x0000019D1CCE4310> File "D:\code\pytest_test\test_2_day\my_test_02_02.py", line 13, in my_function return 1 / (x + y + z) │ │ └ 0 │ └ 0 └ 0 ZeroDivisionError: division by zero
Ok, let's learn it~, we will encapsulate this log function method next.
We created one under the project directory: public_fun\log_fun.py name whatever you want
from loguru import logger import os # get project path def get_pro_path(): pro_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) return pro_path def get_logger(): logger.add(os.path.join(get_pro_path(), "mytest.log"), rotation="500 MB") return logger
We create a test case test_02_04.py
""" loguru use """ from public_fun.log_fun import get_logger from assertpy import assert_that logger = get_logger() def test_fun1(): logger.info('I need to compare 1') assert_that(1).is_not_equal_to(1)
The content of mytest.log appears under the project directory is
2022-07-07 23:48:06.382 | INFO | test_02_02:test_fun1:10 - I need to compare 1
This will automatically bring the file\method\line number that calls him.
it's really awesome
If it doesn't feel great, please use the logging module and look at it later
2022-07-07 23:51