Hướng dẫn pytest python - trăn pytest

Hướng dẫn sử dụng thư viện pytest

1. Cài đặt thư viện pytest:

Vào cmd gõ: py – m pip install pytest

2. Chạy các file test và các phương thức test:

Theo mặc định thì pytest sẽ chỉ nhận dạng được các file có tên bắt đầu với test_ hoặc kết thúc với _test. Còn đối với các phương thức thì yêu cầu tên của các phương thức bắt đầu bằng “test”, những phương thức có tên khác sẽ bị bỏ qua.

Dưới đây là ví dụ về đặt tên file pytest hợp lệ và không hợp lệ:

test_username.py --- hợp lệ

username_test.py --- hợp lệ

testusername --- không hợp lệ

usernametest --- không hợp lệ

Và ví dụ về tên các phương thức pytest hợp lệ và không hợp lệ:

def test_method(): --- hợp lệ

def testmethod(): --- hợp lệ

def method(): ---không hợp lệ

3. Chạy những test được chỉ định:

Chúng ta sẽ sử dụng pytest markers bằng cách định nghĩa các marker trên mỗi phương thức, cách khai báo như sau:

  •  

Ví dụ tạo 2 file test test_1.py và test_2.py có nội dung như bên dưới, sau đó chỉ chạy những test có mark tên example1

Nội dung file test_1.py

import pytest

  1.  

def test_equal_1():

a = 1

b = 2

assert a==b, "a không bằng b"

  1.  

def test_equal_2():

a = 1

b = 2

assert a==b, "a không bằng b"

def test_equal_2():

import pytest

  1.  

def test_equal_1():

a = 1

b = 2

assert a==b, "a không bằng b"

  1.  

def test_equal_2():

a = 1

b = 2

assert a==b, "a không bằng b"

def test_equal_2():

Nội dung file test_2.py

Sau khi lưu lại 2 file trên, chúng ta sẽ chạy những test có mark tên example1 bằng cách vào đường dẫn nơi đặt 2 file vừa tạo và gõ:

Hướng dẫn pytest python - trăn pytest

Hướng dẫn pytest python - trăn pytest

Hướng dẫn pytest python - trăn pytest

pytest –m example1

Kết quả:

Hướng dẫn pytest python - trăn pytest

Theo như trên hình thì test_1.py có 1 chức năng bị Fail, test_2 có 2 phương thức bị Fail, chỉ ra bị sai ở chỗ nào của phương thức.

Ngoài ra, pytest có cung cấp các mark được định nghĩa sẵn, chúng ta sẽ vào cmd gõ: pytest - -markers

4. Fixtures:

import pytest

  •  

def test_equal_1():

a = 1

b = 2

assert a==b, "a không bằng b"

def test_equal_2():

Nội dung file test_2.py

Sau khi lưu lại 2 file trên, chúng ta sẽ chạy những test có mark tên example1 bằng cách vào đường dẫn nơi đặt 2 file vừa tạo và gõ:

pytest –m example1

Sau khi lưu lại 2 file trên, chúng ta sẽ chạy những test có mark tên example1 bằng cách vào đường dẫn nơi đặt 2 file vừa tạo và gõ:

Hướng dẫn pytest python - trăn pytest

Hướng dẫn pytest python - trăn pytest


pytest –m example1

  • Kết quả:(04.03.2021)
  • Theo như trên hình thì test_1.py có 1 chức năng bị Fail, test_2 có 2 phương thức bị Fail, chỉ ra bị sai ở chỗ nào của phương thức.(03.03.2021)
  • Ngoài ra, pytest có cung cấp các mark được định nghĩa sẵn, chúng ta sẽ vào cmd gõ: pytest - -markers(01.03.2021)
  • 4. Fixtures:(01.03.2021)
  • Fixtures được dùng để khởi tạo các thông số đầu vào, thay vì trong mỗi đoạn test chúng ta đều phải khai báo các giá trị để đưa vào test thì ta chỉ cần khai báo một lần duy nhất, khi muốn sử dụng vào đoạn test nào thì gọi hàm đã được chúng ta đánh dấu là fixture.(24.02.2021)
  • Chúng ta sẽ đi đến ví dụ sau để dễ hình dung hơn(24.02.2021)
  • def input_value():(22.02.2021)
  • input = 10(22.02.2021)

Giới thiệu về Pytest

Đặt vấn đề

Bạn viết 1 service mà nó yêu cầu lấy dữ liệu từ bên thứ ba sau đó phải xử lý dữ liệu nhận được để đưa ra kết quả trả về cho response, giả sử quá trình đó diễn ra rất mất thời gian (tầm 5s). Và bây giờ bạn muốn kiểm thử hoạt động của service của mình nhưng lại không mong muốn nó gọi API của bên thứ 3 liên tục mỗi lần test vì nó sẽ làm cho quá trình test chậm lại, vả lại việc gọi thêm API sẽ đòi hỏi nhiều bước làm của bạn Một trường hợp khác là hàm cần test cần thao tác với database, nhưng ta không được phép thay đổi hay cập nhật vào database đó (vì sẽ làm nguy hiểm đến hệ thống đang chạy). Vậy điều chúng ta cần ở đây chính là việc có thể thay thế việc gọi API hay truy cập database bằng một hành động khác nào đó mà vẫn có dữ liệu trả về tương tự như API, chúng ta tạm gọi đó là việc mock hay còn gọi là patching

Pytest

Không giống như phần lớn các ngôn ngữ lập trình, Python sử dụng 1 thư viện built-in cho việc testting đó là

# application.py 
from time import sleep  
def is_windows():    
    # This sleep could be some complex operation instead
    sleep(5)    
    return True  
def get_operating_system():    
    return 'Windows' if is_windows() else 'Linux'
0 Để cài đặt và sử dụng
# application.py 
from time import sleep  
def is_windows():    
    # This sleep could be some complex operation instead
    sleep(5)    
    return True  
def get_operating_system():    
    return 'Windows' if is_windows() else 'Linux'
0, ta chạy câu lệnh sau:

# application.py 
from time import sleep  
def is_windows():    
    # This sleep could be some complex operation instead
    sleep(5)    
    return True  
def get_operating_system():    
    return 'Windows' if is_windows() else 'Linux'
2

Cách sử dụng của Pytest

Đầu tiên ta cần có một hàm để mang đi test, giả sử nó có tên là

# application.py 
from time import sleep  
def is_windows():    
    # This sleep could be some complex operation instead
    sleep(5)    
    return True  
def get_operating_system():    
    return 'Windows' if is_windows() else 'Linux'
3 Việc ta cần làm là:

  1. Tạo file test với cú pháp bắt buộc: có bắt đầu hoặc kết thúc bằng từ
    # application.py 
    from time import sleep  
    def is_windows():    
        # This sleep could be some complex operation instead
        sleep(5)    
        return True  
    def get_operating_system():    
        return 'Windows' if is_windows() else 'Linux'
    
    4 Ví dụ:
    # application.py 
    from time import sleep  
    def is_windows():    
        # This sleep could be some complex operation instead
        sleep(5)    
        return True  
    def get_operating_system():    
        return 'Windows' if is_windows() else 'Linux'
    
    5
  2. Tạo hàm test với cú pháp bắt đầu bằng từ
    # application.py 
    from time import sleep  
    def is_windows():    
        # This sleep could be some complex operation instead
        sleep(5)    
        return True  
    def get_operating_system():    
        return 'Windows' if is_windows() else 'Linux'
    
    4 Ví dụ:
    # application.py 
    from time import sleep  
    def is_windows():    
        # This sleep could be some complex operation instead
        sleep(5)    
        return True  
    def get_operating_system():    
        return 'Windows' if is_windows() else 'Linux'
    
    7 Sử dụng
    # application.py 
    from time import sleep  
    def is_windows():    
        # This sleep could be some complex operation instead
        sleep(5)    
        return True  
    def get_operating_system():    
        return 'Windows' if is_windows() else 'Linux'
    
    8 để so sánh kết quả khi gọi hàm
    # application.py 
    from time import sleep  
    def is_windows():    
        # This sleep could be some complex operation instead
        sleep(5)    
        return True  
    def get_operating_system():    
        return 'Windows' if is_windows() else 'Linux'
    
    3 với
    # test_application.py
    
    from application import get_operating_system
    
    def test_get_operating_system():
        assert get_operating_system() == 'Windows'
    
    0 ta mong muốn (
    # application.py 
    from time import sleep  
    def is_windows():    
        # This sleep could be some complex operation instead
        sleep(5)    
        return True  
    def get_operating_system():    
        return 'Windows' if is_windows() else 'Linux'
    
    8 sẽ raise lên lỗi khi kết quả không bằng nhau)
  3. Chạy test Sử dụng
    # test_application.py
    
    from application import get_operating_system
    
    def test_get_operating_system():
        assert get_operating_system() == 'Windows'
    
    2 để chạy dòng lệnh sau:

# test_application.py

from application import get_operating_system

def test_get_operating_system():
    assert get_operating_system() == 'Windows'
3

Giải thích:
    Flag `-k` đi kèm với 1 keyword, Pytest sẽ tìm các hàm có tên hàm chứa keyword đó để chạy (rất hữu ích trong trường hợp bạn viết nhiều hàm test cho nhiều API và muốn chạy 1 hàm cụ thể)
    Flag `-v` sẽ hiển thị nhiều thông tin của testcase hơn khi chạy testing
    Flag `-vv` tương tự như `-v` nhưng sẽ hiển thị chi tiết trong trường hợp nếu testcase failed (đầu ra với output khác nhau) sẽ chỉ ra khác nhau ở đoạn nào
    Flag `-s` sẽ thực thi và in ra nội dung của các câu lệnh print trong code (Mà pytest sẽ bỏ qua lệnh print)

Ví dụ đơn giản về sử dụng Pytest

Ví dụ này sẽ giúp bạn hiểu tại sao nên sử dụng mock để có thể tăng tốc độ testing và không ảnh hưởng đến code base mà bạn đã viết Giả sử ta có hàm

# test_application.py

from application import get_operating_system

def test_get_operating_system():
    assert get_operating_system() == 'Windows'
5 sẽ cho ta biết máy tính đang sử dụng Windows hay Linux

# application.py 
from time import sleep  
def is_windows():    
    # This sleep could be some complex operation instead
    sleep(5)    
    return True  
def get_operating_system():    
    return 'Windows' if is_windows() else 'Linux'

Trong đó, hàm

# test_application.py

from application import get_operating_system

def test_get_operating_system():
    assert get_operating_system() == 'Windows'
6 để check nếu hệ điều hành hiện tại là Windows hay không, ta giả sử hàm này khá phức tạp và tốn khá nhiều thời gian để chạy, ta giả sử bằng hàm sleep(5), nghĩa là chương trình sẽ mất 5s để xử lý Hàm
# test_application.py

from application import get_operating_system

def test_get_operating_system():
    assert get_operating_system() == 'Windows'
5 sẽ là phần code base và ta cần phải mang nó đi test để kiểm thử Ta cần một hàm chạy test, có tên là
# test_application.py

from application import get_operating_system

def test_get_operating_system():
    assert get_operating_system() == 'Windows'
8 sau:

# test_application.py

from application import get_operating_system

def test_get_operating_system():
    assert get_operating_system() == 'Windows'

Sau đó ta chạy lệnh test:

$ pytest
================ test session starts ========================
Python 3.7.3, pytest-5.4.1, py-1.8.1, pluggy-0.13.1
rootdir: /usr/Personal/Projects/pytest-and-mocking
plugins: mock-2.0.0
collected 1 item

test_application.py .                                    [100%]

================ 1 passed in 5.05s ==========================

Ta thấy kết quả chạy thực sự khá lâu, và để khắc phục điều đó, ta sẽ sử dụng khái niệm gọi là

# test_application.py

from application import get_operating_system

def test_get_operating_system():
    assert get_operating_system() == 'Windows'
9 và để nó là tham số của hàm test, nó có tác dụng làm test của ta khi gọi đến hàm
$ pytest
================ test session starts ========================
Python 3.7.3, pytest-5.4.1, py-1.8.1, pluggy-0.13.1
rootdir: /usr/Personal/Projects/pytest-and-mocking
plugins: mock-2.0.0
collected 1 item

test_application.py .                                    [100%]

================ 1 passed in 5.05s ==========================
0 sẽ không chạy hàm thật, mà return luôn về giá trị mà ta định sẵn trong đối số của
# test_application.py

from application import get_operating_system

def test_get_operating_system():
    assert get_operating_system() == 'Windows'
9
# test_application.py

from application import get_operating_system

def test_get_operating_system():
    assert get_operating_system() == 'Windows'
9 sẽ nhận 2 đối số, một là
$ pytest
================ test session starts ========================
Python 3.7.3, pytest-5.4.1, py-1.8.1, pluggy-0.13.1
rootdir: /usr/Personal/Projects/pytest-and-mocking
plugins: mock-2.0.0
collected 1 item

test_application.py .                                    [100%]

================ 1 passed in 5.05s ==========================
3 - là đường dẫn đến nơi hàm mà ta định fake hàm, hai là
$ pytest
================ test session starts ========================
Python 3.7.3, pytest-5.4.1, py-1.8.1, pluggy-0.13.1
rootdir: /usr/Personal/Projects/pytest-and-mocking
plugins: mock-2.0.0
collected 1 item

test_application.py .                                    [100%]

================ 1 passed in 5.05s ==========================
4 là giá trị trả về khi gọi hàm fake đó Bây giờ ta sẽ update lại hàm test, sử dụng
# test_application.py

from application import get_operating_system

def test_get_operating_system():
    assert get_operating_system() == 'Windows'
9:

# 'mocker' fixture provided by pytest-mock
def test_get_operating_system(mocker):  
    # Mock the slow function and return True always
    mocker.patch('application.is_windows', return_value=True) 
    assert get_operating_system() == 'Windows'

Ta chạy lại code test sẽ thấy kết quả được cải thiện:

$ pytest
============ test session starts ==================
Python 3.7.3, pytest-5.4.1, py-1.8.1, pluggy-0.13.1
rootdir: /mnt/c/Personal/Projects/pytest-and-mocking
plugins: mock-2.0.0
collected 1 item

test_application.py .                          [100%]

=========== 1 passed in 0.11s ======================

Trong ví dụ trên ta mặc định những gì ta trả về chỉ là giá trị

$ pytest
================ test session starts ========================
Python 3.7.3, pytest-5.4.1, py-1.8.1, pluggy-0.13.1
rootdir: /usr/Personal/Projects/pytest-and-mocking
plugins: mock-2.0.0
collected 1 item

test_application.py .                                    [100%]

================ 1 passed in 5.05s ==========================
6, giờ muốn nó là
$ pytest
================ test session starts ========================
Python 3.7.3, pytest-5.4.1, py-1.8.1, pluggy-0.13.1
rootdir: /usr/Personal/Projects/pytest-and-mocking
plugins: mock-2.0.0
collected 1 item

test_application.py .                                    [100%]

================ 1 passed in 5.05s ==========================
7 thì sẽ viết như sau:

def test_operation_system_is_linux(mocker):
    mocker.patch('application.is_windows', return_value=False) # set the return value to be False
    assert get_operating_system() == 'Linux'

Tài liệu tham khảo

https://www.freblogg.com/pytest-functions-mocking-1