doctest --- Kiểm tra các ví dụ Python tương tác

Source code: Lib/doctest.py


Mô-đun doctest tìm kiếm các đoạn văn bản trông giống như các phiên Python tương tác, sau đó thực thi các phiên đó để xác minh rằng chúng hoạt động chính xác như được hiển thị. Có một số cách phổ biến để sử dụng doctest:

  • Để kiểm tra xem chuỗi tài liệu của mô-đun có được cập nhật hay không bằng cách xác minh rằng tất cả các ví dụ tương tác vẫn hoạt động như tài liệu.

  • Để thực hiện kiểm tra hồi quy bằng cách xác minh rằng các ví dụ tương tác từ tệp kiểm tra hoặc đối tượng kiểm tra hoạt động như mong đợi.

  • Để viết tài liệu hướng dẫn cho một gói, được minh họa một cách phóng khoáng bằng các ví dụ đầu vào-đầu ra. Tùy thuộc vào việc các ví dụ hoặc văn bản giải thích có được nhấn mạnh hay không, điều này có tính chất "kiểm tra khả năng đọc viết" hoặc "tài liệu có thể thực thi".

Đây là một mô-đun ví dụ hoàn chỉnh nhưng nhỏ:

"""
Đây là mô-đun "ví dụ".

Mô-đun ví dụ cung cấp một hàm, giai thừa().  Ví dụ,

>>> giai thừa(5)
120
"""

giai thừa def (n):
    """Trả về giai thừa của n, một số nguyên chính xác >= 0.

    >>> [giai thừa(n) cho n trong phạm vi(6)]
    [1, 1, 2, 6, 24, 120]
    >>> giai thừa(30)
    265252859812191058636308480000000
    >>> giai thừa(-1)
    Traceback (cuộc gọi gần đây nhất):
        ...
    Giá trịLỗi: n phải >= 0

    Giai thừa của số float là được, nhưng số float phải là số nguyên chính xác:
    >>> giai thừa(30.1)
    Traceback (cuộc gọi gần đây nhất):
        ...
    ValueError: n phải là số nguyên chính xác
    >>> giai thừa(30.0)
    265252859812191058636308480000000

    Nó cũng không được lớn một cách lố bịch:
    >>> giai thừa(1e100)
    Traceback (cuộc gọi gần đây nhất):
        ...
    TrànLỗi: n quá lớn
    """

    nhập toán
    nếu không n >= 0:
        tăng ValueError("n phải >= 0")
    nếu math.floor(n) != n:
        raise ValueError("n phải là số nguyên chính xác")
    nếu n+1 == n: # catch một giá trị như 1e300
        tăng OverflowError ("n quá lớn")
    kết quả = 1
    hệ số = 2
    trong khi hệ số <= n:
        kết quả *= hệ số
        hệ số += 1
    kết quả trả về


nếu __name__ == "__main__":
    nhập doctest
    doctest.testmod()

Nếu bạn chạy example.py trực tiếp từ dòng lệnh, doctest sẽ hoạt động kỳ diệu:

$ python example.py
$

Không có đầu ra! Điều đó là bình thường và có nghĩa là tất cả các ví dụ đều hoạt động. Chuyển -v vào tập lệnh và doctest in nhật ký chi tiết về những gì nó đang cố gắng và in bản tóm tắt ở cuối:

$ python example.py -v
Đang cố gắng:
    giai thừa(5)
Đang mong đợi:
    120
được
Đang cố gắng:
    [giai thừa(n) cho n trong phạm vi(6)]
Đang mong đợi:
    [1, 1, 2, 6, 24, 120]
được

Và cứ thế, cuối cùng kết thúc bằng:

Đang cố gắng:
    giai thừa(1e100)
Đang mong đợi:
    Traceback (cuộc gọi gần đây nhất):
        ...
    TrànLỗi: n quá lớn
được
2 mục đã vượt qua tất cả các bài kiểm tra:
   1 bài kiểm tra trong __main__
   6 bài kiểm tra trong __main__.factorial
7 bài kiểm tra trong 2 mục.
7 đã trôi qua.
Thử nghiệm đã trôi qua.
$

Đó là tất cả những gì bạn cần biết để bắt đầu sử dụng doctest một cách hiệu quả! Hãy tham gia. Các phần sau đây cung cấp đầy đủ thông tin chi tiết. Lưu ý rằng có nhiều ví dụ về doctest trong bộ thư viện và bộ thử nghiệm Python tiêu chuẩn. Các ví dụ đặc biệt hữu ích có thể được tìm thấy trong tệp thử nghiệm tiêu chuẩn Lib/test/test_doctest/test_doctest.py.

Added in version 3.13: Đầu ra được tô màu theo mặc định và có thể là controlled using environment variables.

Cách sử dụng đơn giản: Kiểm tra các ví dụ trong Docstrings

Cách đơn giản nhất để bắt đầu sử dụng doctest (nhưng không nhất thiết phải theo cách bạn sẽ tiếp tục làm) là kết thúc mỗi mô-đun M bằng:

nếu __name__ == "__main__":
    nhập doctest
    doctest.testmod()

doctest sau đó kiểm tra các chuỗi tài liệu trong mô-đun M.

Việc chạy mô-đun dưới dạng tập lệnh sẽ khiến các ví dụ trong chuỗi tài liệu được thực thi và xác minh:

trăn M.py

Điều này sẽ không hiển thị bất cứ điều gì trừ khi một ví dụ bị lỗi, trong trường hợp đó (các) ví dụ bị lỗi và (các) nguyên nhân của (các) lỗi được in ra thiết bị xuất chuẩn và dòng đầu ra cuối cùng là ***Test Failed*** N failures., trong đó N là số lượng các ví dụ bị lỗi.

Thay vào đó hãy chạy nó bằng công tắc -v

trăn M.py -v

và một báo cáo chi tiết về tất cả các ví dụ đã thử sẽ được in ra đầu ra tiêu chuẩn, cùng với các loại tóm tắt ở cuối.

Bạn có thể buộc chế độ dài dòng bằng cách chuyển verbose=True sang testmod() hoặc cấm chế độ này bằng cách chuyển verbose=False. Trong cả hai trường hợp đó, sys.argv không được testmod() kiểm tra (vì vậy việc vượt qua -v hay không đều không có hiệu lực).

Ngoài ra còn có một phím tắt dòng lệnh để chạy testmod(), xem phần Sử dụng dòng lệnh.

Để biết thêm thông tin về testmod(), hãy xem phần API cơ bản.

Cách sử dụng đơn giản: Kiểm tra các ví dụ trong tệp văn bản

Một ứng dụng đơn giản khác của doctest là kiểm tra các ví dụ tương tác trong một tệp văn bản. Điều này có thể được thực hiện với chức năng testfile():

nhập doctest
doctest.testfile("example.txt")

Tập lệnh ngắn đó thực thi và xác minh mọi ví dụ Python tương tác có trong tệp example.txt. Nội dung tệp được xử lý như thể nó là một chuỗi tài liệu khổng lồ; tệp không cần chứa chương trình Python! Ví dụ: có lẽ example.txt chứa cái này:

Mô-đun ``example``
========================

Sử dụng ``factorial``
-------------------

Đây là một tệp văn bản mẫu ở định dạng reStructuredText.  Lần nhập đầu tiên
``factorial`` từ mô-đun ``example``:

    >>> từ ví dụ nhập giai thừa

Bây giờ sử dụng nó:

    >>> giai thừa(6)
    120

Chạy doctest.testfile("example.txt") thì thấy lỗi trong tài liệu này

Tệp "./example.txt", dòng 14, trong example.txt
 dụ không thành công:
    giai thừa(6)
Dự kiến:
    120
:
    720

Như với testmod(), testfile() sẽ không hiển thị bất cứ thứ gì trừ khi một ví dụ bị lỗi. Nếu một ví dụ không thành công thì (các) ví dụ bị lỗi và (các) nguyên nhân của (các) lỗi đó sẽ được in ra thiết bị xuất chuẩn, sử dụng cùng định dạng như testmod().

Theo mặc định, testfile() tìm kiếm các tập tin trong thư mục của mô-đun gọi điện. Xem phần API cơ bản để biết mô tả về các đối số tùy chọn có thể được sử dụng để yêu cầu nó tìm kiếm tệp ở các vị trí khác.

Giống như testmod(), mức độ chi tiết của testfile() có thể được đặt bằng khóa chuyển dòng lệnh -v hoặc bằng đối số từ khóa tùy chọn verbose.

Ngoài ra còn có một phím tắt dòng lệnh để chạy testfile(), xem phần Sử dụng dòng lệnh.

Để biết thêm thông tin về testfile(), hãy xem phần API cơ bản.

Sử dụng dòng lệnh

Mô-đun doctest có thể được gọi dưới dạng tập lệnh từ dòng lệnh:

python -m doctest [-v] [-o OPTION] [-f] tệp [tệp ...]
-v, --verbose

Báo cáo chi tiết của tất cả các ví dụ đã thử được in ra đầu ra tiêu chuẩn, cùng với các loại tóm tắt ở cuối:

python -m doctest -v example.py

Điều này sẽ nhập example.py dưới dạng mô-đun độc lập và chạy testmod() trên đó. Lưu ý rằng điều này có thể không hoạt động chính xác nếu tệp là một phần của gói và nhập các mô-đun con khác từ gói đó.

Nếu tên tệp không kết thúc bằng .py, thì doctest sẽ suy ra rằng nó phải được chạy bằng testfile():

python -m doctest -v example.txt
-o, --option <option>

Cờ tùy chọn kiểm soát các khía cạnh khác nhau trong hành vi của doctest, xem phần Cờ tùy chọn.

Added in version 3.4.

-f, --fail-fast

Đây là cách viết tắt của -o FAIL_FAST.

Added in version 3.4.

Nó hoạt động như thế nào

Phần này xem xét chi tiết cách thức hoạt động của doctest: nó xem xét chuỗi tài liệu nào, cách nó tìm thấy các ví dụ tương tác, bối cảnh thực thi mà nó sử dụng, cách nó xử lý các ngoại lệ và cách các cờ tùy chọn có thể được sử dụng để kiểm soát hành vi của nó. Đây là thông tin bạn cần biết để viết những ví dụ hay nhất; để biết thông tin về việc thực sự chạy doctest trên các ví dụ này, hãy xem các phần sau.

Những tài liệu nào được kiểm tra?

Chuỗi tài liệu mô-đun và tất cả các chuỗi tài liệu hàm, lớp và phương thức đều được tìm kiếm. Các đối tượng được nhập vào mô-đun không được tìm kiếm.

Ngoài ra, có những trường hợp bạn muốn các bài kiểm tra là một phần của mô-đun nhưng không phải là một phần của văn bản trợ giúp, điều này yêu cầu các bài kiểm tra không được đưa vào chuỗi tài liệu. Doctest tìm kiếm một biến cấp mô-đun có tên là __test__ và sử dụng nó để xác định các thử nghiệm khác. Nếu M.__test__ tồn tại, nó phải là một lệnh chính tả và mỗi mục nhập ánh xạ một tên (chuỗi) tới một đối tượng hàm, đối tượng lớp hoặc chuỗi. Các chuỗi tài liệu đối tượng hàm và lớp được tìm thấy từ M.__test__ sẽ được tìm kiếm và các chuỗi được xử lý như thể chúng là các chuỗi tài liệu. Trong đầu ra, khóa K trong M.__test__ xuất hiện với tên M.__test__.K.

Ví dụ: đặt khối mã này ở đầu example.py:

__kiểm tra__ = {
    'số': """
>>> giai thừa(6)
720

>>> [giai thừa(n) cho n trong phạm vi(6)]
[1, 1, 2, 6, 24, 120]
"""
}

Giá trị của example.__test__["numbers"] sẽ được coi là một chuỗi tài liệu và tất cả các thử nghiệm bên trong nó sẽ được chạy. Điều quan trọng cần lưu ý là giá trị có thể được ánh xạ tới một hàm, đối tượng lớp hoặc mô-đun; nếu vậy, doctest sẽ tìm kiếm các chuỗi tài liệu một cách đệ quy, sau đó sẽ được quét để kiểm tra.

Bất kỳ lớp nào được tìm thấy đều được tìm kiếm đệ quy theo cách tương tự, để kiểm tra các chuỗi tài liệu trong các phương thức được chứa và các lớp lồng nhau của chúng.

Ghi chú

doctest chỉ có thể tự động khám phá các lớp và hàm được xác định ở cấp mô-đun hoặc bên trong các lớp khác.

Vì các lớp và hàm lồng nhau chỉ tồn tại khi hàm bên ngoài được gọi nên chúng không thể được phát hiện. Xác định chúng bên ngoài để làm cho chúng hiển thị.

Các ví dụ về chuỗi tài liệu được công nhận như thế nào?

Trong hầu hết các trường hợp, việc sao chép và dán phiên bảng điều khiển tương tác đều hoạt động tốt, nhưng doctest không cố gắng thực hiện mô phỏng chính xác bất kỳ trình bao Python cụ thể nào.

>>> # comments bị bỏ qua
>>> x = 12
>>>x
12
>>> nếu x == 13:
... in("có")
... khác:
... in("không")
... in("KHÔNG")
... in("KHÔNG!!!")
...
không
KHÔNG
KHÔNG!!!
>>>

Mọi đầu ra dự kiến phải ngay sau dòng '>>> ' hoặc '... ' cuối cùng chứa mã và đầu ra dự kiến (nếu có) sẽ mở rộng đến dòng '>>> ' hoặc toàn khoảng trắng tiếp theo.

Bản in đẹp:

  • Đầu ra dự kiến không thể chứa một dòng toàn khoảng trắng, vì dòng như vậy được dùng để báo hiệu sự kết thúc của đầu ra dự kiến. Nếu đầu ra dự kiến ​​chứa một dòng trống, hãy đặt <BLANKLINE> vào ví dụ tài liệu tốt nhất của bạn mỗi nơi dự kiến ​​sẽ có một dòng trống.

  • Tất cả các ký tự tab cứng đều được mở rộng thành khoảng trắng, sử dụng các điểm dừng tab 8 cột. Các tab trong đầu ra do mã kiểm tra tạo ra không được sửa đổi. Bởi vì bất kỳ tab cứng nào trong đầu ra mẫu are đều được mở rộng, điều này có nghĩa là nếu đầu ra mã bao gồm các tab cứng, cách duy nhất doctest có thể vượt qua là nếu tùy chọn NORMALIZE_WHITESPACE hoặc directive có hiệu lực. Ngoài ra, bài kiểm tra có thể được viết lại để nắm bắt kết quả đầu ra và so sánh nó với giá trị mong đợi như một phần của bài kiểm tra. Việc xử lý các tab trong nguồn này được thực hiện thông qua quá trình thử và sai và đã được chứng minh là cách xử lý chúng ít xảy ra lỗi nhất. Có thể sử dụng một thuật toán khác để xử lý các tab bằng cách viết một lớp DocTestParser tùy chỉnh.

  • Đầu ra cho thiết bị xuất chuẩn được ghi lại, nhưng không xuất ra thiết bị xuất chuẩn (các dấu vết ngoại lệ được ghi lại thông qua một phương tiện khác).

  • Nếu bạn tiếp tục một dòng thông qua dấu gạch chéo ngược trong phiên tương tác hoặc vì bất kỳ lý do nào khác mà sử dụng dấu gạch chéo ngược, bạn nên sử dụng chuỗi tài liệu thô, chuỗi này sẽ giữ nguyên dấu gạch chéo ngược chính xác khi bạn nhập chúng:

    >>> định nghĩa f(x):
    ... r'''Dấu gạch chéo ngược trong chuỗi tài liệu thô: m\n'''
    ...
    >>> in(f.__doc__)
    Dấu gạch chéo ngược trong chuỗi tài liệu thô: m\n
    

    Nếu không, dấu gạch chéo ngược sẽ được hiểu là một phần của chuỗi. Ví dụ: \n ở trên sẽ được hiểu là ký tự dòng mới. Ngoài ra, bạn có thể nhân đôi mỗi dấu gạch chéo ngược trong phiên bản doctest (và không sử dụng chuỗi thô):

    >>> định nghĩa f(x):
    ... '''Dấu gạch chéo ngược trong chuỗi tài liệu thô: m\\n'''
    ...
    >>> in(f.__doc__)
    Dấu gạch chéo ngược trong chuỗi tài liệu thô: m\n
    
  • Cột bắt đầu không quan trọng:

    >>> khẳng định "Dễ dàng!"
          >>> nhập toán
              >>> math.floor(1.9)
              1
    

    và nhiều ký tự khoảng trắng ở đầu bị loại bỏ khỏi kết quả mong đợi như đã xuất hiện trong dòng '>>> ' ban đầu bắt đầu ví dụ.

Bối cảnh thực thi là gì?

Theo mặc định, mỗi lần doctest tìm thấy một chuỗi tài liệu để kiểm tra, nó sẽ sử dụng một chuỗi tài liệu shallow copy trong số các toàn cục của M để việc chạy thử nghiệm không làm thay đổi các chuỗi toàn cục thực của mô-đun và do đó một thử nghiệm trong M không thể để lại các mẩu vụn vô tình cho phép một thử nghiệm khác hoạt động. Điều này có nghĩa là các ví dụ có thể tự do sử dụng bất kỳ tên nào được xác định ở cấp cao nhất trong M và các tên được xác định trước đó trong chuỗi tài liệu đang được chạy. Ví dụ không thể thấy tên được xác định trong các chuỗi tài liệu khác.

Thay vào đó, bạn có thể buộc sử dụng lệnh của riêng mình làm bối cảnh thực thi bằng cách chuyển globs=your_dict sang testmod() hoặc testfile().

Điều gì về ngoại lệ?

Không vấn đề gì, miễn là truy nguyên là đầu ra duy nhất được tạo ra bởi ví dụ: chỉ cần dán vào truy nguyên. [1] Vì các dấu vết chứa các chi tiết có khả năng thay đổi nhanh chóng (ví dụ: đường dẫn tệp và số dòng chính xác), đây là một trường hợp mà doctest cố gắng hết sức để linh hoạt trong những gì nó chấp nhận.

Ví dụ đơn giản:

>>> [1, 2, 3].remove(42)
Traceback (cuộc gọi gần đây nhất):
  Tệp "<stdin>", dòng 1, trong <module>
ValueError: list.remove(x): x không có trong danh sách

Doctest đó thành công nếu ValueError được nâng lên, với chi tiết list.remove(x): x not in list như được hiển thị.

Đầu ra dự kiến cho một ngoại lệ phải bắt đầu bằng tiêu đề truy nguyên, có thể là một trong hai dòng sau, được thụt lề giống như dòng đầu tiên của ví dụ:

Traceback (cuộc gọi gần đây nhất):
Traceback (trong cùng cuối cùng):

Tiêu đề truy nguyên được theo sau bởi một ngăn xếp truy nguyên tùy chọn, nội dung của nó bị doctest bỏ qua. Ngăn xếp truy nguyên thường bị bỏ qua hoặc được sao chép nguyên văn từ một phiên tương tác.

Tiếp theo ngăn xếp truy nguyên là phần thú vị nhất: (các) dòng chứa loại ngoại lệ và chi tiết. Đây thường là dòng cuối cùng của truy nguyên, nhưng có thể mở rộng trên nhiều dòng nếu ngoại lệ có chi tiết nhiều dòng:

>>> tăng ValueError('multi\n line\ndetail')
Traceback (cuộc gọi gần đây nhất):
  Tệp "<stdin>", dòng 1, trong <module>
Giá trịLỗi: đa
    dòng
chi tiết

Ba dòng cuối cùng (bắt đầu bằng ValueError) được so sánh với loại và chi tiết của ngoại lệ, phần còn lại bị bỏ qua.

Cách thực hành tốt nhất là bỏ qua ngăn xếp truy nguyên, trừ khi nó bổ sung thêm giá trị tài liệu quan trọng vào ví dụ. Vì vậy, ví dụ cuối cùng có lẽ tốt hơn là:

>>> tăng ValueError('multi\n line\ndetail')
Traceback (cuộc gọi gần đây nhất):
    ...
Giá trịLỗi: đa
    dòng
chi tiết

Lưu ý rằng việc truy nguyên được xử lý rất đặc biệt. Đặc biệt, trong ví dụ viết lại, việc sử dụng ... độc lập với tùy chọn ELLIPSIS của doctest. Dấu chấm lửng trong ví dụ đó có thể bị bỏ đi hoặc cũng có thể là ba (hoặc ba trăm) dấu phẩy hoặc chữ số hoặc bản ghi thụt lề của một tiểu phẩm Monty Python.

Một số chi tiết bạn nên đọc một lần nhưng sẽ không cần phải nhớ:

  • Doctest không thể đoán liệu kết quả mong đợi của bạn đến từ việc truy nguyên ngoại lệ hay từ quá trình in thông thường. Vì vậy, ví dụ: một ví dụ mong đợi ValueError: 42 is prime sẽ vượt qua dù ValueError có thực sự được nâng lên hay nếu ví dụ đó chỉ in văn bản truy nguyên đó. Trong thực tế, đầu ra thông thường hiếm khi bắt đầu bằng dòng tiêu đề truy ngược, do đó điều này không tạo ra vấn đề thực sự.

  • Mỗi dòng của ngăn xếp truy ngược (nếu có) phải được thụt lề xa hơn dòng đầu tiên của ví dụ, or bắt đầu bằng ký tự không phải chữ và số. Dòng đầu tiên sau tiêu đề truy nguyên được thụt lề giống nhau và bắt đầu bằng chữ và số được coi là phần bắt đầu của chi tiết ngoại lệ. Tất nhiên điều này là đúng đối với các hoạt động truy nguyên chính hãng.

  • Khi tùy chọn doctest IGNORE_EXCEPTION_DETAIL được chỉ định, mọi thứ sau dấu hai chấm ngoài cùng bên trái và mọi thông tin mô-đun trong tên ngoại lệ đều bị bỏ qua.

  • Shell tương tác bỏ qua dòng tiêu đề truy nguyên đối với một số SyntaxErrors. Nhưng doctest sử dụng dòng tiêu đề truy nguyên để phân biệt các trường hợp ngoại lệ với không phải ngoại lệ. Vì vậy, trong trường hợp hiếm hoi khi bạn cần kiểm tra một SyntaxError bỏ qua tiêu đề traceback, bạn sẽ cần thêm dòng tiêu đề traceback vào ví dụ kiểm tra của mình theo cách thủ công.

  • Đối với một số trường hợp ngoại lệ, Python hiển thị vị trí lỗi bằng cách sử dụng dấu ^ và dấu ngã:

    >>> 1 + Không
      Tệp "<stdin>", dòng 1
        1 + Không
        ~~^~~~~~~
    TypeError: (các) loại toán hạng không được hỗ trợ cho +: 'int' và 'NoneType'
    

    Vì các dòng hiển thị vị trí lỗi xuất hiện trước loại và chi tiết ngoại lệ nên chúng không được doctest kiểm tra. Ví dụ: bài kiểm tra sau sẽ đạt, mặc dù nó đặt điểm đánh dấu ^ ở sai vị trí:

    >>> 1 + Không
      Tệp "<stdin>", dòng 1
        1 + Không
        ^~~~~~~~~
    TypeError: (các) loại toán hạng không được hỗ trợ cho +: 'int' và 'NoneType'
    

Cờ tùy chọn

Một số cờ tùy chọn kiểm soát các khía cạnh khác nhau trong hành vi của doctest. Tên tượng trưng cho các cờ được cung cấp dưới dạng hằng số mô-đun, có thể là bitwise ORed cùng nhau và được chuyển đến các hàm khác nhau. Tên cũng có thể được sử dụng trong doctest directives và có thể được chuyển đến giao diện dòng lệnh doctest thông qua tùy chọn -o.

Nhóm tùy chọn đầu tiên xác định ngữ nghĩa kiểm tra, kiểm soát các khía cạnh về cách doctest quyết định xem đầu ra thực tế có khớp với đầu ra dự kiến ​​của một ví dụ hay không:

doctest.DONT_ACCEPT_TRUE_FOR_1

Theo mặc định, nếu khối đầu ra dự kiến chỉ chứa 1 thì khối đầu ra thực tế chỉ chứa 1 hoặc chỉ True được coi là phù hợp và tương tự đối với 0 so với False. Khi DONT_ACCEPT_TRUE_FOR_1 được chỉ định, không được phép thay thế. Hành vi mặc định phục vụ cho việc Python đã thay đổi kiểu trả về của nhiều hàm từ số nguyên sang boolean; doctests mong đợi đầu ra "số nguyên nhỏ" vẫn hoạt động trong những trường hợp này. Tùy chọn này có thể sẽ biến mất, nhưng không phải trong vài năm nữa.

doctest.DONT_ACCEPT_BLANKLINE

Theo mặc định, nếu khối đầu ra dự kiến chứa một dòng chỉ chứa chuỗi <BLANKLINE> thì dòng đó sẽ khớp với một dòng trống trong đầu ra thực tế. Bởi vì một dòng trống thực sự sẽ phân định kết quả đầu ra dự kiến, đây là cách duy nhất để thông báo rằng một dòng trống được mong đợi. Khi DONT_ACCEPT_BLANKLINE được chỉ định, sự thay thế này không được phép.

doctest.NORMALIZE_WHITESPACE

Khi được chỉ định, tất cả các chuỗi khoảng trắng (khoảng trống và dòng mới) đều được coi là bằng nhau. Bất kỳ chuỗi khoảng trắng nào trong kết quả đầu ra dự kiến ​​sẽ khớp với bất kỳ chuỗi khoảng trắng nào trong kết quả đầu ra thực tế. Theo mặc định, khoảng trắng phải khớp chính xác. NORMALIZE_WHITESPACE đặc biệt hữu ích khi một dòng đầu ra dự kiến ​​rất dài và bạn muốn gói nó thành nhiều dòng trong nguồn của mình.

doctest.ELLIPSIS

Khi được chỉ định, dấu chấm lửng (...) trong đầu ra dự kiến có thể khớp với bất kỳ chuỗi con nào trong đầu ra thực tế. Điều này bao gồm các chuỗi con mở rộng ranh giới dòng và các chuỗi con trống, vì vậy tốt nhất bạn nên sử dụng chuỗi này một cách đơn giản. Việc sử dụng phức tạp có thể dẫn đến cùng một kiểu "ôi, nó khớp quá nhiều!" những điều ngạc nhiên mà .* thường gặp phải trong các biểu thức thông thường.

doctest.IGNORE_EXCEPTION_DETAIL

Khi được chỉ định, các doctest mong đợi ngoại lệ sẽ vượt qua miễn là ngoại lệ của loại dự kiến ​​được nêu ra, ngay cả khi các chi tiết (thông báo và tên ngoại lệ đủ điều kiện) không khớp.

Ví dụ: một ví dụ mong đợi ValueError: 42 sẽ vượt qua nếu ngoại lệ thực tế được đưa ra là ValueError: 3*14, nhưng sẽ thất bại nếu thay vào đó, một TypeError được đưa ra. Nó cũng sẽ bỏ qua mọi tên đủ điều kiện được đưa vào trước lớp ngoại lệ, tên này có thể khác nhau giữa các cách triển khai và phiên bản Python cũng như mã/thư viện đang sử dụng. Do đó, cả ba biến thể này sẽ hoạt động với cờ được chỉ định:

>>> tăng Ngoại lệ ('tin nhắn')
Traceback (cuộc gọi gần đây nhất):
Ngoại lệ: tin nhắn

>>> tăng Ngoại lệ ('tin nhắn')
Traceback (cuộc gọi gần đây nhất):
nội dung.Exception: tin nhắn

>>> tăng Ngoại lệ ('tin nhắn')
Traceback (cuộc gọi gần đây nhất):
__main__.Ngoại lệ: tin nhắn

Lưu ý rằng ELLIPSIS cũng có thể được sử dụng để bỏ qua các chi tiết của thông báo ngoại lệ, nhưng việc kiểm tra như vậy vẫn có thể thất bại dựa trên việc tên mô-đun có xuất hiện hay khớp chính xác hay không.

Thay đổi trong phiên bản 3.2: IGNORE_EXCEPTION_DETAIL hiện cũng bỏ qua mọi thông tin liên quan đến mô-đun chứa ngoại lệ đang được thử nghiệm.

doctest.SKIP

Khi được chỉ định, không chạy ví dụ nào cả. Điều này có thể hữu ích trong các bối cảnh trong đó các ví dụ doctest đóng vai trò vừa là tài liệu vừa là trường hợp kiểm thử, đồng thời nên đưa vào một ví dụ cho mục đích ghi lại tài liệu nhưng không nên kiểm tra. Ví dụ: đầu ra của ví dụ có thể là ngẫu nhiên; hoặc ví dụ có thể phụ thuộc vào các tài nguyên không có sẵn cho trình điều khiển thử nghiệm.

Cờ SKIP cũng có thể được sử dụng cho các ví dụ "bình luận" tạm thời.

doctest.COMPARISON_FLAGS

Một bitmask hoặc kết hợp tất cả các cờ so sánh ở trên.

Nhóm tùy chọn thứ hai kiểm soát cách báo cáo lỗi kiểm tra:

doctest.REPORT_UDIFF

Khi được chỉ định, các lỗi liên quan đến đầu ra thực tế và dự kiến nhiều dòng sẽ được hiển thị bằng cách sử dụng khác biệt thống nhất.

doctest.REPORT_CDIFF

Khi được chỉ định, các lỗi liên quan đến đầu ra thực tế và dự kiến nhiều dòng sẽ được hiển thị bằng cách sử dụng ngữ cảnh khác nhau.

doctest.REPORT_NDIFF

Khi được chỉ định, sự khác biệt sẽ được difflib.Differ tính toán, sử dụng thuật toán tương tự như tiện ích ndiff.py phổ biến. Đây là phương pháp duy nhất đánh dấu sự khác biệt giữa các dòng cũng như giữa các dòng. Ví dụ: nếu một dòng đầu ra dự kiến ​​chứa chữ số 1 trong đó đầu ra thực tế chứa chữ cái l, thì một dòng sẽ được chèn bằng dấu mũ đánh dấu các vị trí cột không khớp.

doctest.REPORT_ONLY_FIRST_FAILURE

Khi được chỉ định, hãy hiển thị ví dụ bị lỗi đầu tiên trong mỗi doctest, nhưng chặn đầu ra đối với tất cả các ví dụ còn lại. Điều này sẽ ngăn doctest báo cáo các ví dụ chính xác bị hỏng do lỗi trước đó; nhưng nó cũng có thể che giấu các ví dụ không chính xác bị lỗi độc lập với lỗi đầu tiên. Khi REPORT_ONLY_FIRST_FAILURE được chỉ định, các mẫu còn lại vẫn chạy và vẫn được tính vào tổng số lỗi được báo cáo; chỉ có đầu ra bị triệt tiêu.

doctest.FAIL_FAST

Khi được chỉ định, hãy thoát sau ví dụ lỗi đầu tiên và không cố chạy các ví dụ còn lại. Do đó, số lỗi được báo cáo sẽ nhiều nhất là 1. Cờ này có thể hữu ích trong quá trình gỡ lỗi, vì các ví dụ sau lỗi đầu tiên thậm chí sẽ không tạo ra kết quả gỡ lỗi.

doctest.REPORTING_FLAGS

Một bitmask hoặc kết hợp tất cả các cờ báo cáo ở trên.

Ngoài ra còn có một cách để đăng ký tên cờ tùy chọn mới, mặc dù điều này không hữu ích trừ khi bạn có ý định mở rộng nội bộ doctest thông qua phân lớp:

doctest.register_optionflag(name)

Tạo cờ tùy chọn mới với tên đã cho và trả về giá trị số nguyên của cờ mới. register_optionflag() có thể được sử dụng khi phân lớp OutputChecker hoặc DocTestRunner để tạo các tùy chọn mới được các lớp con của bạn hỗ trợ. register_optionflag() phải luôn được gọi bằng cách sử dụng thành ngữ sau

MY_FLAG = register_optionflag('MY_FLAG')

Chỉ thị

Các chỉ thị Doctest có thể được sử dụng để sửa đổi option flags cho một ví dụ riêng lẻ. Các lệnh Doctest là các nhận xét Python đặc biệt theo mã nguồn của một ví dụ:

directive:             "#" "doctest:" directive_options
directive_options:     directive_option ("," directive_option)*
directive_option:      on_or_off directive_option_name
on_or_off:             "+" | "-"
directive_option_name: "DONT_ACCEPT_BLANKLINE" | "NORMALIZE_WHITESPACE" | ...

Không được phép có khoảng trắng giữa + hoặc - và tên tùy chọn chỉ thị. Tên tùy chọn chỉ thị có thể là bất kỳ tên cờ tùy chọn nào được giải thích ở trên.

Các chỉ thị doctest của một ví dụ sửa đổi hành vi của doctest cho ví dụ đó. Sử dụng + để kích hoạt hành vi được đặt tên hoặc - để tắt nó.

Ví dụ: bài kiểm tra này đạt:

>>> print(list(range(20))) # doctest: +NORMALIZE_WHITESPACE
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
10, 11, 12, 13, 14, 15, 16, 17, 18, 19]

Nếu không có lệnh này thì nó sẽ thất bại, cả vì đầu ra thực tế không có hai khoảng trống trước các phần tử danh sách một chữ số và vì đầu ra thực tế nằm trên một dòng. Bài kiểm tra này cũng vượt qua và cũng yêu cầu chỉ thị để thực hiện:

>>> print(list(range(20))) # doctest: +ELLIPSIS
[0, 1, ..., 18, 19]

Nhiều lệnh có thể được sử dụng trên một dòng vật lý, được phân tách bằng dấu phẩy:

>>> print(list(range(20))) # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
[0, 1, ..., 18, 19]

Nếu nhiều nhận xét chỉ thị được sử dụng cho một ví dụ thì chúng sẽ được kết hợp:

>>> print(list(range(20))) # doctest: +ELLIPSIS
... # doctest: +NORMALIZE_WHITESPACE
[0, 1, ..., 18, 19]

Như ví dụ trước cho thấy, bạn có thể thêm các dòng ... vào ví dụ chỉ chứa các lệnh. Điều này có thể hữu ích khi một ví dụ quá dài để một lệnh có thể vừa vặn trên cùng một dòng:

>>> print(list(range(5)) + list(range(10, 20)) + list(range(30, 40)))
... # doctest: +ELLIPSIS
[0, ..., 4, 10, ..., 19, 30, ..., 39]

Lưu ý rằng vì tất cả các tùy chọn đều bị tắt theo mặc định và các lệnh chỉ áp dụng cho ví dụ mà chúng xuất hiện, nên việc bật các tùy chọn (thông qua + trong một lệnh) thường là lựa chọn có ý nghĩa duy nhất. Tuy nhiên, cờ tùy chọn cũng có thể được chuyển đến các hàm chạy doctest, thiết lập các giá trị mặc định khác nhau. Trong những trường hợp như vậy, việc tắt tùy chọn qua - trong một lệnh có thể hữu ích.

Cảnh báo

doctest rất nghiêm túc trong việc yêu cầu kết quả đầu ra phải khớp chính xác. Nếu thậm chí một ký tự không khớp, thử nghiệm sẽ thất bại. Điều này có thể sẽ làm bạn ngạc nhiên đôi khi vì bạn tìm hiểu chính xác những gì Python làm và không đảm bảo về đầu ra. Ví dụ: khi in một tập hợp, Python không đảm bảo rằng phần tử đó được in theo bất kỳ thứ tự cụ thể nào, do đó, hãy thử nghiệm như

>>> foo()
{"thư rác", "trứng"}

dễ bị tổn thương! Một cách giải quyết khác là làm

>>> foo() == {"spam", "trứng"}
đúng

thay vào đó. Một cách khác là làm

>>> d = đã sắp xếp(foo())
>>> d
['trứng', 'thư rác']

Có những người khác, nhưng bạn có được ý tưởng.

Một ý tưởng tồi khác là in những thứ nhúng địa chỉ đối tượng, như

>>> id(1.0) # certain đôi khi bị lỗi
7948648
>>> lớp C: đậu
>>> C() # the Repr mặc định() cho các trường hợp nhúng địa chỉ
<Đối tượng C tại 0x00AC18F0>

Lệnh ELLIPSIS đưa ra một cách tiếp cận hay cho ví dụ cuối cùng:

>>> C() # doctest: +ELLIPSIS
<Đối tượng C ở 0x...>

Các số có dấu phẩy động cũng có thể có sự thay đổi đầu ra nhỏ trên các nền tảng, vì Python sử dụng thư viện nền tảng C để thực hiện một số phép tính dấu phẩy động và các thư viện C ở đây có chất lượng rất khác nhau.

>>> 1000**0,1 # risky
1.9952623149688797
>>> vòng(1000**0.1, 9) # safer
1.995262315
>>> print(f'{1000**0.1:.4f}') # much an toàn hơn
1.9953

Các số có dạng I/2.**J an toàn trên tất cả các nền tảng và tôi thường tạo ra các ví dụ tốt nhất để tạo ra các số có dạng đó:

>>> 3./4 # utterly an toàn
0,75

Các phân số đơn giản cũng dễ hiểu hơn đối với mọi người và điều đó tạo nên tài liệu tốt hơn.

API cơ bản

Các hàm testmod()testfile() cung cấp một giao diện đơn giản cho doctest, đủ cho hầu hết các mục đích sử dụng cơ bản. Để có phần giới thiệu ít trang trọng hơn về hai hàm này, hãy xem phần Cách sử dụng đơn giản: Kiểm tra các ví dụ trong DocstringsCách sử dụng đơn giản: Kiểm tra các ví dụ trong tệp văn bản.

doctest.testfile(filename, module_relative=True, name=None, package=None, globs=None, verbose=None, report=True, optionflags=0, extraglobs=None, raise_on_error=False, parser=DocTestParser(), encoding=None)

Tất cả các đối số ngoại trừ filename đều là tùy chọn và phải được chỉ định ở dạng từ khóa.

Ví dụ thử nghiệm trong tệp có tên filename. Trả về (failure_count, test_count).

Đối số tùy chọn module_relative chỉ định cách diễn giải tên tệp:

  • Nếu module_relativeTrue (mặc định), thì filename chỉ định đường dẫn tương đối mô-đun độc lập với hệ điều hành. Theo mặc định, đường dẫn này liên quan đến thư mục của mô-đun gọi điện; nhưng nếu đối số package được chỉ định thì nó có liên quan đến gói đó. Để đảm bảo tính độc lập của hệ điều hành, filename nên sử dụng các ký tự / để phân tách các đoạn đường dẫn và có thể không phải là đường dẫn tuyệt đối (tức là nó không được bắt đầu bằng /).

  • Nếu module_relativeFalse thì filename chỉ định đường dẫn dành riêng cho hệ điều hành. Đường dẫn có thể tuyệt đối hoặc tương đối; đường dẫn tương đối được giải quyết đối với thư mục làm việc hiện tại.

Đối số tùy chọn name đưa ra tên của bài kiểm tra; theo mặc định hoặc nếu None, os.path.basename(filename) được sử dụng.

Đối số tùy chọn package là gói Python hoặc tên của gói Python có thư mục nên được sử dụng làm thư mục cơ sở cho tên tệp liên quan đến mô-đun. Nếu không có gói nào được chỉ định thì thư mục của mô-đun gọi sẽ được sử dụng làm thư mục cơ sở cho tên tệp liên quan đến mô-đun. Sẽ có lỗi khi chỉ định package nếu module_relativeFalse.

Đối số tùy chọn globs đưa ra một lệnh được sử dụng làm toàn cục khi thực thi các ví dụ. Một bản sao nông mới của lệnh này được tạo cho doctest, vì vậy các ví dụ của nó bắt đầu bằng một bản rõ ràng. Theo mặc định hoặc nếu None, một lệnh trống mới sẽ được sử dụng.

Đối số tùy chọn extraglobs đưa ra một lệnh được hợp nhất vào các phần tổng thể được sử dụng để thực thi các ví dụ. Điều này hoạt động giống như dict.update(): nếu globsextraglobs có một khóa chung, giá trị liên quan trong extraglobs sẽ xuất hiện trong lệnh kết hợp. Theo mặc định hoặc nếu None, không có tổng thể bổ sung nào được sử dụng. Đây là một tính năng nâng cao cho phép tham số hóa các doctest. Ví dụ: một doctest có thể được viết cho một lớp cơ sở, sử dụng tên chung cho lớp đó, sau đó được sử dụng lại để kiểm tra bất kỳ số lượng lớp con nào bằng cách chuyển một lệnh extraglobs ánh xạ tên chung cho lớp con cần kiểm tra.

Đối số tùy chọn verbose in nhiều nội dung nếu đúng và chỉ in lỗi nếu sai; theo mặc định hoặc nếu None, điều đó đúng khi và chỉ khi '-v' nằm trong sys.argv.

Đối số tùy chọn report in bản tóm tắt ở cuối khi đúng, nếu không thì không in gì ở cuối. Ở chế độ dài dòng, bản tóm tắt rất chi tiết, nếu không thì bản tóm tắt rất ngắn gọn (trên thực tế, sẽ trống nếu tất cả các bài kiểm tra đều vượt qua).

Đối số tùy chọn optionflags (giá trị mặc định 0) lấy bitwise OR của các cờ tùy chọn. Xem phần Cờ tùy chọn.

Đối số tùy chọn raise_on_error mặc định là sai. Nếu đúng, một ngoại lệ sẽ được đưa ra khi xảy ra lỗi đầu tiên hoặc ngoại lệ không mong muốn trong một ví dụ. Điều này cho phép các lỗi được gỡ lỗi sau khi chết. Hành vi mặc định là tiếp tục chạy các ví dụ.

Đối số tùy chọn parser chỉ định một DocTestParser (hoặc lớp con) sẽ được sử dụng để trích xuất các bài kiểm tra từ các tệp. Nó mặc định là một trình phân tích cú pháp bình thường (tức là DocTestParser()).

Đối số tùy chọn encoding chỉ định mã hóa sẽ được sử dụng để chuyển đổi tệp sang unicode.

doctest.testmod(m=None, name=None, globs=None, verbose=None, report=True, optionflags=0, extraglobs=None, raise_on_error=False, exclude_empty=False)

Tất cả các đối số đều là tùy chọn và tất cả ngoại trừ m phải được chỉ định ở dạng từ khóa.

Ví dụ kiểm tra trong chuỗi tài liệu trong các hàm và lớp có thể truy cập được từ mô-đun m (hoặc mô-đun __main__ nếu m không được cung cấp hoặc là None), bắt đầu bằng m.__doc__.

Đồng thời kiểm tra các ví dụ có thể truy cập được từ dict m.__test__, nếu nó tồn tại. m.__test__ ánh xạ tên (chuỗi) thành các hàm, lớp và chuỗi; các chuỗi tài liệu về hàm và lớp được tìm kiếm để lấy ví dụ; các chuỗi được tìm kiếm trực tiếp, như thể chúng là các chuỗi tài liệu.

Chỉ các chuỗi tài liệu gắn liền với các đối tượng thuộc mô-đun m mới được tìm kiếm.

Trả về (failure_count, test_count).

Đối số tùy chọn name cung cấp tên của mô-đun; theo mặc định hoặc nếu None, m.__name__ được sử dụng.

Đối số tùy chọn exclude_empty mặc định là sai. Nếu đúng, các đối tượng không tìm thấy doctest sẽ bị loại khỏi việc xem xét. Mặc định là hack tương thích ngược, do đó mã vẫn sử dụng doctest.master.summarize kết hợp với testmod() tiếp tục nhận đầu ra cho các đối tượng không có kiểm tra. Đối số exclude_empty cho hàm tạo DocTestFinder mới hơn được mặc định là true.

Các đối số tùy chọn extraglobs, verbose, report, optionflags, raise_on_errorglobs giống như đối với hàm testfile() ở trên, ngoại trừ việc globs mặc định là m.__dict__.

doctest.run_docstring_examples(f, globs, verbose=False, name='NoName', compileflags=None, optionflags=0)

Các ví dụ thử nghiệm liên quan đến đối tượng f; ví dụ: f có thể là một chuỗi, một mô-đun, một hàm hoặc một đối tượng lớp.

Một bản sao nông của đối số từ điển globs được sử dụng cho ngữ cảnh thực thi.

Đối số tùy chọn name được sử dụng trong các thông báo lỗi và mặc định là "NoName".

Nếu đối số tùy chọn verbose là đúng, đầu ra sẽ được tạo ngay cả khi không có lỗi nào. Theo mặc định, đầu ra chỉ được tạo trong trường hợp mẫu bị lỗi.

Đối số tùy chọn compileflags cung cấp tập hợp các cờ sẽ được trình biên dịch Python sử dụng khi chạy các ví dụ. Theo mặc định, hoặc nếu None, các cờ được suy ra tương ứng với tập hợp các tính năng trong tương lai được tìm thấy trong globs.

Đối số tùy chọn optionflags hoạt động như đối với hàm testfile() ở trên.

API đơn giản nhất

Khi bộ sưu tập các mô-đun doctest của bạn tăng lên, bạn sẽ muốn có một cách để chạy tất cả các doctest của chúng một cách có hệ thống. doctest cung cấp hai chức năng có thể được sử dụng để tạo bộ thử nghiệm unittest từ các mô-đun và tệp văn bản chứa doctest. Để tích hợp với khám phá thử nghiệm unittest, hãy bao gồm chức năng load_tests trong mô-đun thử nghiệm của bạn:

nhập khẩu đơn vị nhất
nhập doctest
nhập my_module_with_doctests

def Load_tests(loader, kiểm tra, bỏ qua):
    test.addTests(doctest.DocTestSuite(my_module_with_doctests))
    trả lại bài kiểm tra

Có hai chức năng chính để tạo phiên bản unittest.TestSuite từ tệp văn bản và mô-đun bằng doctest:

doctest.DocFileSuite(*paths, module_relative=True, package=None, setUp=None, tearDown=None, globs=None, optionflags=0, parser=DocTestParser(), encoding=None)

Chuyển đổi các bài kiểm tra doctest từ một hoặc nhiều tệp văn bản sang unittest.TestSuite.

Zz000zz được trả về sẽ được chạy bởi khung nhỏ nhất và chạy các ví dụ tương tác trong mỗi tệp. Nếu một ví dụ trong bất kỳ tệp nào không thành công thì thử nghiệm đơn vị tổng hợp sẽ không thành công và ngoại lệ failureException sẽ xuất hiện hiển thị tên của tệp chứa thử nghiệm và số dòng (đôi khi gần đúng). Nếu tất cả các ví dụ trong một tệp bị bỏ qua thì bài kiểm tra đơn vị tổng hợp cũng được đánh dấu là bị bỏ qua.

Truyền một hoặc nhiều đường dẫn (dưới dạng chuỗi) tới tệp văn bản cần kiểm tra.

Các tùy chọn có thể được cung cấp dưới dạng đối số từ khóa:

Đối số tùy chọn module_relative chỉ định cách diễn giải tên tệp trong paths:

  • Nếu module_relativeTrue (mặc định), thì mỗi tên tệp trong paths chỉ định một đường dẫn tương đối mô-đun độc lập với hệ điều hành. Theo mặc định, đường dẫn này liên quan đến thư mục của mô-đun gọi điện; nhưng nếu đối số package được chỉ định thì nó có liên quan đến gói đó. Để đảm bảo tính độc lập với hệ điều hành, mỗi tên tệp phải sử dụng các ký tự / để phân tách các đoạn đường dẫn và không được là đường dẫn tuyệt đối (tức là tên tệp không được bắt đầu bằng /).

  • Nếu module_relativeFalse thì mỗi tên tệp trong paths chỉ định một đường dẫn dành riêng cho hệ điều hành. Đường dẫn có thể tuyệt đối hoặc tương đối; đường dẫn tương đối được giải quyết đối với thư mục làm việc hiện tại.

Đối số tùy chọn package là gói Python hoặc tên của gói Python có thư mục nên được sử dụng làm thư mục cơ sở cho tên tệp liên quan đến mô-đun trong paths. Nếu không có gói nào được chỉ định thì thư mục của mô-đun gọi sẽ được sử dụng làm thư mục cơ sở cho tên tệp liên quan đến mô-đun. Sẽ có lỗi khi chỉ định package nếu module_relativeFalse.

Đối số tùy chọn setUp chỉ định chức năng thiết lập cho bộ thử nghiệm. Điều này được gọi trước khi chạy thử nghiệm trong mỗi tệp. Hàm setUp sẽ được truyền vào đối tượng DocTest. Hàm setUp có thể truy cập vào tổng thể thử nghiệm khi thuộc tính globs của thử nghiệm đã vượt qua.

Đối số tùy chọn tearDown chỉ định chức năng phân tích cho bộ thử nghiệm. Điều này được gọi sau khi chạy thử nghiệm trong mỗi tệp. Hàm tearDown sẽ được truyền vào đối tượng DocTest. Hàm tearDown có thể truy cập vào tổng thể thử nghiệm khi thuộc tính globs của thử nghiệm đã vượt qua.

Đối số tùy chọn globs là một từ điển chứa các biến toàn cục ban đầu cho các bài kiểm tra. Một bản sao mới của từ điển này được tạo cho mỗi bài kiểm tra. Theo mặc định, globs là một từ điển trống mới.

Đối số tùy chọn optionflags chỉ định các tùy chọn doctest mặc định cho các bài kiểm tra, được tạo bằng cách kết hợp các cờ tùy chọn riêng lẻ với nhau. Xem phần Cờ tùy chọn. Xem chức năng set_unittest_reportflags() bên dưới để biết cách đặt tùy chọn báo cáo tốt hơn.

Đối số tùy chọn parser chỉ định một DocTestParser (hoặc lớp con) sẽ được sử dụng để trích xuất các bài kiểm tra từ các tệp. Nó mặc định là một trình phân tích cú pháp bình thường (tức là DocTestParser()).

Đối số tùy chọn encoding chỉ định mã hóa sẽ được sử dụng để chuyển đổi tệp sang unicode.

__file__ toàn cục được thêm vào các tổng thể được cung cấp cho các tài liệu được tải từ tệp văn bản bằng DocFileSuite().

doctest.DocTestSuite(module=None, globs=None, extraglobs=None, test_finder=None, setUp=None, tearDown=None, optionflags=0, checker=None)

Chuyển đổi các bài kiểm tra doctest cho một mô-đun thành unittest.TestSuite.

Zz000zz được trả về sẽ được chạy bởi khung nhỏ nhất và chạy từng doctest trong mô-đun. Mỗi chuỗi tài liệu được chạy dưới dạng thử nghiệm đơn vị riêng biệt. Nếu bất kỳ tài liệu nào không thành công thì bài kiểm tra đơn vị tổng hợp sẽ không thành công và một ngoại lệ unittest.TestCase.failureException sẽ xuất hiện hiển thị tên của tệp chứa bài kiểm tra và số dòng (đôi khi gần đúng). Nếu tất cả các ví dụ trong chuỗi tài liệu bị bỏ qua thì

Đối số tùy chọn module cung cấp mô-đun cần kiểm tra. Nó có thể là một đối tượng mô-đun hoặc tên mô-đun (có thể có dấu chấm). Nếu không được chỉ định, mô-đun gọi hàm này sẽ được sử dụng.

Đối số tùy chọn globs là một từ điển chứa các biến toàn cục ban đầu cho các bài kiểm tra. Một bản sao mới của từ điển này được tạo cho mỗi bài kiểm tra. Theo mặc định, globs__dict__ của mô-đun.

Đối số tùy chọn extraglobs chỉ định một tập hợp các biến toàn cục bổ sung, được hợp nhất thành globs. Theo mặc định, không có hình cầu bổ sung nào được sử dụng.

Đối số tùy chọn test_finder là đối tượng DocTestFinder (hoặc một đối tượng thay thế thả vào) được sử dụng để trích xuất các tài liệu từ mô-đun.

Các đối số tùy chọn setUp, tearDownoptionflags giống như đối với hàm DocFileSuite() ở trên, nhưng chúng được gọi cho mỗi chuỗi tài liệu.

Chức năng này sử dụng kỹ thuật tìm kiếm tương tự như testmod().

Thay đổi trong phiên bản 3.5: DocTestSuite() trả về một unittest.TestSuite trống nếu module không chứa chuỗi tài liệu thay vì tăng ValueError.

Dưới vỏ bọc, DocTestSuite() tạo ra một unittest.TestSuite từ các phiên bản doctest.DocTestCaseDocTestCase là một lớp con của unittest.TestCase. DocTestCase không được ghi lại ở đây (đây là chi tiết nội bộ), nhưng việc nghiên cứu mã của nó có thể trả lời các câu hỏi về chi tiết chính xác của việc tích hợp unittest.

Tương tự, DocFileSuite() tạo ra một unittest.TestSuite từ các phiên bản doctest.DocFileCaseDocFileCase là một lớp con của DocTestCase.

Vì vậy, cả hai cách tạo unittest.TestSuite đều chạy phiên bản DocTestCase. Điều này quan trọng vì một lý do tế nhị: khi bạn tự chạy các hàm doctest, bạn có thể kiểm soát trực tiếp các tùy chọn doctest đang được sử dụng bằng cách chuyển cờ tùy chọn cho các hàm doctest. Tuy nhiên, nếu bạn đang viết khung unittest, unittest sẽ kiểm soát thời điểm và cách thức chạy thử nghiệm. Tác giả khung thường muốn kiểm soát các tùy chọn báo cáo doctest (có thể, ví dụ: được chỉ định bởi các tùy chọn dòng lệnh), nhưng không có cách nào để chuyển các tùy chọn thông qua unittest sang các trình chạy thử nghiệm doctest.

Vì lý do này, doctest cũng hỗ trợ khái niệm cờ báo cáo doctest dành riêng cho hỗ trợ unittest, thông qua chức năng này:

doctest.set_unittest_reportflags(flags)

Đặt cờ báo cáo doctest để sử dụng.

Đối số flags lấy bitwise OR của các cờ tùy chọn. Xem phần Cờ tùy chọn. Chỉ có thể sử dụng "cờ báo cáo".

Đây là cài đặt toàn mô-đun và ảnh hưởng đến tất cả các tài liệu trong tương lai do mô-đun unittest chạy: phương thức runTest() của DocTestCase xem xét các cờ tùy chọn được chỉ định cho trường hợp thử nghiệm khi phiên bản DocTestCase được tạo. Nếu không có cờ báo cáo nào được chỉ định (đó là trường hợp điển hình và được mong đợi), các cờ báo cáo unittest của doctest sẽ được bitwise ORed đưa vào các cờ tùy chọn và các cờ tùy chọn được tăng cường như vậy sẽ được chuyển đến phiên bản DocTestRunner được tạo để chạy doctest. Nếu bất kỳ cờ báo cáo nào được chỉ định khi phiên bản DocTestCase được tạo, thì cờ báo cáo unittest của doctest sẽ bị bỏ qua.

Giá trị của cờ báo cáo unittest có hiệu lực trước khi hàm được gọi sẽ được hàm trả về.

API nâng cao

Zz000zz cơ bản là một trình bao bọc đơn giản nhằm mục đích giúp doctest dễ sử dụng. Nó khá linh hoạt và đáp ứng hầu hết nhu cầu của người dùng; tuy nhiên, nếu bạn yêu cầu kiểm soát chi tiết hơn đối với việc kiểm tra hoặc muốn mở rộng khả năng của doctest thì bạn nên sử dụng API nâng cao.

Zz000zz nâng cao xoay quanh hai lớp vùng chứa, được sử dụng để lưu trữ các ví dụ tương tác được trích xuất từ ​​các trường hợp doctest:

  • Example: Một statement Python duy nhất, được ghép nối với đầu ra dự kiến của nó.

  • DocTest: Một tập hợp các Examples, thường được trích xuất từ một chuỗi tài liệu hoặc tệp văn bản.

Các lớp xử lý bổ sung được xác định để tìm, phân tích cú pháp và chạy cũng như kiểm tra các ví dụ tốt nhất:

  • DocTestFinder: Tìm tất cả các chuỗi tài liệu trong một mô-đun nhất định và sử dụng DocTestParser để tạo DocTest từ mọi chuỗi tài liệu có chứa các ví dụ tương tác.

  • DocTestParser: Tạo đối tượng DocTest từ một chuỗi (chẳng hạn như chuỗi tài liệu của đối tượng).

  • DocTestRunner: Thực thi các ví dụ trong DocTest và sử dụng OutputChecker để xác minh đầu ra của chúng.

  • OutputChecker: So sánh kết quả thực tế từ một ví dụ doctest với kết quả dự kiến và quyết định xem chúng có khớp hay không.

Mối quan hệ giữa các lớp xử lý này được tóm tắt trong sơ đồ sau:

danh sách:
+------+ +----------+
|module| --DocTestFinder-> | DocTest | --DocTestRunner-> kết quả
+------+ |        ^     +---------+     | ^ (đã in)
            |        | | Example | |       |
            v |     | ... |     v       |
           Trình kiểm tra đầu ra DocTestParser | Example |
                           +----------+

Đối tượng DocTest

class doctest.DocTest(examples, globs, name, filename, lineno, docstring)

Một tập hợp các ví dụ tốt nhất nên được chạy trong một không gian tên duy nhất. Các đối số của hàm tạo được sử dụng để khởi tạo các thuộc tính có cùng tên.

DocTest xác định các thuộc tính sau. Chúng được khởi tạo bởi hàm tạo và không được sửa đổi trực tiếp.

examples

Danh sách các đối tượng Example mã hóa các ví dụ Python tương tác riêng lẻ sẽ được chạy trong thử nghiệm này.

globs

Không gian tên (còn gọi là toàn cầu) mà các ví dụ sẽ được chạy trong đó. Đây là từ điển ánh xạ tên tới các giá trị. Mọi thay đổi đối với không gian tên do các ví dụ thực hiện (chẳng hạn như liên kết các biến mới) sẽ được phản ánh trong globs sau khi chạy thử nghiệm.

name

Tên chuỗi xác định DocTest. Thông thường, đây là tên của đối tượng hoặc tệp mà thử nghiệm được trích xuất từ ​​đó.

filename

Tên của tệp mà DocTest này được trích xuất từ ​​đó; hoặc None nếu không xác định được tên tệp hoặc nếu DocTest không được trích xuất từ ​​tệp.

lineno

Số dòng trong filename nơi DocTest này bắt đầu hoặc None nếu không có số dòng. Số dòng này dựa trên số 0 so với phần đầu của tệp.

docstring

Chuỗi mà bài kiểm tra được trích xuất từ đó hoặc None nếu chuỗi đó không có sẵn hoặc nếu bài kiểm tra không được trích xuất từ một chuỗi.

Đối tượng mẫu

class doctest.Example(source, want, exc_msg=None, lineno=0, indent=0, options=None)

Một ví dụ tương tác duy nhất, bao gồm một câu lệnh Python và kết quả mong đợi của nó. Các đối số của hàm tạo được sử dụng để khởi tạo các thuộc tính có cùng tên.

Example xác định các thuộc tính sau. Chúng được khởi tạo bởi hàm tạo và không được sửa đổi trực tiếp.

source

Một chuỗi chứa mã nguồn của ví dụ. Mã nguồn này bao gồm một câu lệnh Python duy nhất và luôn kết thúc bằng một dòng mới; hàm tạo thêm một dòng mới khi cần thiết.

want

Kết quả mong đợi từ việc chạy mã nguồn của ví dụ (từ thiết bị xuất chuẩn hoặc truy nguyên trong trường hợp ngoại lệ). want kết thúc bằng một dòng mới trừ khi không có đầu ra nào được mong đợi, trong trường hợp đó đó là một chuỗi trống. Hàm tạo thêm một dòng mới khi cần thiết.

exc_msg

Thông báo ngoại lệ do ví dụ tạo ra, nếu ví dụ đó dự kiến sẽ tạo ra ngoại lệ; hoặc None nếu dự kiến ​​nó không tạo ra ngoại lệ. Thông báo ngoại lệ này được so sánh với giá trị trả về của traceback.format_exception_only(). exc_msg kết thúc bằng một dòng mới trừ khi đó là None. Hàm tạo thêm một dòng mới nếu cần.

lineno

Số dòng trong chuỗi chứa ví dụ này nơi ví dụ bắt đầu. Số dòng này dựa trên số 0 so với phần đầu của chuỗi chứa.

indent

Thụt lề của ví dụ trong chuỗi chứa, tức là số ký tự khoảng trắng đứng trước dấu nhắc đầu tiên của ví dụ.

options

Ánh xạ từ điển từ cờ tùy chọn sang True hoặc False, được sử dụng để ghi đè các tùy chọn mặc định cho ví dụ này. Bất kỳ cờ tùy chọn nào không có trong từ điển này đều được giữ ở giá trị mặc định (như được chỉ định bởi optionflags của DocTestRunner). Theo mặc định, không có tùy chọn nào được đặt.

Đối tượng DocTestFinder

class doctest.DocTestFinder(verbose=False, parser=DocTestParser(), recurse=True, exclude_empty=True)

Một lớp xử lý được sử dụng để trích xuất các DocTests có liên quan đến một đối tượng nhất định, từ chuỗi tài liệu của nó và các chuỗi tài liệu của các đối tượng được chứa trong đó. DocTests có thể được trích xuất từ ​​các mô-đun, lớp, hàm, phương thức, phương thức tĩnh, phương thức lớp và thuộc tính.

Đối số tùy chọn verbose có thể được sử dụng để hiển thị các đối tượng được tìm kiếm bởi công cụ tìm kiếm. Nó mặc định là False (không có đầu ra).

Đối số tùy chọn parser chỉ định đối tượng DocTestParser (hoặc một đối tượng thay thế thả vào) được sử dụng để trích xuất các tài liệu từ chuỗi tài liệu.

Nếu đối số tùy chọn recurse là sai thì DocTestFinder.find() sẽ chỉ kiểm tra đối tượng đã cho chứ không kiểm tra bất kỳ đối tượng nào được chứa.

Nếu đối số tùy chọn exclude_empty là sai thì DocTestFinder.find() sẽ bao gồm các kiểm tra đối với các đối tượng có chuỗi tài liệu trống.

DocTestFinder định nghĩa phương thức sau:

find(obj[, name][, module][, globs][, extraglobs])

Trả về danh sách DocTests được xác định bởi chuỗi tài liệu của obj hoặc bởi bất kỳ chuỗi tài liệu nào của đối tượng chứa nó.

Đối số tùy chọn name chỉ định tên của đối tượng; tên này sẽ được sử dụng để tạo tên cho DocTests được trả về. Nếu name không được chỉ định thì obj.__name__ sẽ được sử dụng.

Tham số tùy chọn module là mô-đun chứa đối tượng đã cho. Nếu mô-đun không được chỉ định hoặc là None thì công cụ tìm kiếm sẽ cố gắng tự động xác định đúng mô-đun. Mô-đun của đối tượng được sử dụng:

  • Là không gian tên mặc định, nếu globs không được chỉ định.

  • Để ngăn DocTestFinder trích xuất DocTest từ các đối tượng được nhập từ các mô-đun khác. (Các đối tượng chứa mô-đun không phải là module sẽ bị bỏ qua.)

  • Để tìm tên của tập tin chứa đối tượng.

  • Để giúp tìm số dòng của đối tượng trong tệp của nó.

Nếu moduleFalse thì sẽ không có nỗ lực tìm kiếm mô-đun nào được thực hiện. Điều này không rõ ràng, chủ yếu được sử dụng trong việc kiểm tra doctest: nếu moduleFalse hoặc là None nhưng không thể được tìm thấy tự động, thì tất cả các đối tượng được coi là thuộc về mô-đun (không tồn tại), vì vậy tất cả các đối tượng được chứa sẽ được tìm kiếm (đệ quy) để tìm doctest.

Các giá trị chung cho mỗi DocTest được hình thành bằng cách kết hợp globsextraglobs (các liên kết trong extraglobs ghi đè các liên kết trong globs). Một bản sao nông mới của từ điển toàn cầu được tạo cho mỗi DocTest. Nếu globs không được chỉ định thì nó sẽ mặc định là __dict__ của mô-đun nếu được chỉ định hoặc {} nếu không. Nếu extraglobs không được chỉ định thì mặc định là {}.

Đối tượng DocTestParser

class doctest.DocTestParser

Một lớp xử lý được sử dụng để trích xuất các ví dụ tương tác từ một chuỗi và sử dụng chúng để tạo đối tượng DocTest.

DocTestParser định nghĩa các phương thức sau:

get_doctest(string, globs, name, filename, lineno)

Trích xuất tất cả các ví dụ doctest từ chuỗi đã cho và thu thập chúng vào đối tượng DocTest.

globs, name, filenamelineno là các thuộc tính cho đối tượng DocTest mới. Xem tài liệu về DocTest để biết thêm thông tin.

get_examples(string, name='<string>')

Trích xuất tất cả các ví dụ doctest từ chuỗi đã cho và trả về chúng dưới dạng danh sách các đối tượng Example. Số dòng dựa trên 0. Đối số tùy chọn name là tên xác định chuỗi này và chỉ được sử dụng cho các thông báo lỗi.

parse(string, name='<string>')

Chia chuỗi đã cho thành các ví dụ và văn bản xen vào, rồi trả về chúng dưới dạng danh sách các chuỗi và Examples xen kẽ. Số dòng cho Examples dựa trên 0. Đối số tùy chọn name là tên xác định chuỗi này và chỉ được sử dụng cho các thông báo lỗi.

Đối tượng TestResults

class doctest.TestResults(failed, attempted)
failed

Số lần thử nghiệm thất bại.

attempted

Số lượng thử nghiệm đã thử.

skipped

Số bài kiểm tra bị bỏ qua

Added in version 3.13.

Đối tượng DocTestRunner

class doctest.DocTestRunner(checker=None, verbose=None, optionflags=0)

Lớp xử lý được sử dụng để thực thi và xác minh các ví dụ tương tác trong DocTest.

Việc so sánh giữa kết quả đầu ra dự kiến và kết quả đầu ra thực tế được thực hiện bởi OutputChecker. Sự so sánh này có thể được tùy chỉnh bằng một số cờ tùy chọn; xem phần Cờ tùy chọn để biết thêm thông tin. Nếu cờ tùy chọn không đủ thì việc so sánh cũng có thể được tùy chỉnh bằng cách chuyển một lớp con của OutputChecker cho hàm tạo.

Đầu ra màn hình của người chạy thử có thể được điều khiển theo hai cách. Đầu tiên, một hàm đầu ra có thể được chuyển tới run(); hàm này sẽ được gọi với các chuỗi sẽ được hiển thị. Nó mặc định là sys.stdout.write. Nếu việc thu thập đầu ra là không đủ thì đầu ra màn hình cũng có thể được tùy chỉnh bằng cách phân lớp con DocTestRunner và ghi đè các phương thức report_start(), report_success(), report_unexpected_exception()report_failure().

Đối số từ khóa tùy chọn checker chỉ định đối tượng OutputChecker (hoặc thay thế thả vào) sẽ được sử dụng để so sánh kết quả đầu ra dự kiến với kết quả đầu ra thực tế của các ví dụ doctest.

Đối số từ khóa tùy chọn verbose kiểm soát mức độ chi tiết của DocTestRunner. Nếu verboseTrue thì thông tin sẽ được in về từng ví dụ khi nó được chạy. Nếu verboseFalse thì chỉ in lỗi. Nếu verbose không được chỉ định hoặc None thì đầu ra chi tiết sẽ được sử dụng nếu sử dụng khóa chuyển dòng lệnh -v.

Đối số từ khóa tùy chọn optionflags có thể được sử dụng để kiểm soát cách người chạy thử so sánh đầu ra dự kiến với đầu ra thực tế và cách nó hiển thị các lỗi. Để biết thêm thông tin, xem phần Cờ tùy chọn.

Người chạy thử tích lũy số liệu thống kê. Số lượng tổng hợp các ví dụ đã thử, không thành công và bị bỏ qua cũng có sẵn thông qua các thuộc tính tries, failuresskips. Các phương thức run()summarize() trả về một phiên bản TestResults.

DocTestRunner định nghĩa các phương thức sau:

report_start(out, test, example)

Báo cáo rằng người chạy thử sắp xử lý ví dụ đã cho. Phương thức này được cung cấp để cho phép các lớp con của DocTestRunner tùy chỉnh đầu ra của chúng; nó không nên được gọi trực tiếp.

example là ví dụ sắp được xử lý. test là bài kiểm tra có chứa example. out là hàm đầu ra được truyền cho DocTestRunner.run().

report_success(out, test, example, got)

Báo cáo rằng ví dụ đã cho đã chạy thành công. Phương thức này được cung cấp để cho phép các lớp con của DocTestRunner tùy chỉnh đầu ra của chúng; nó không nên được gọi trực tiếp.

example là ví dụ sắp được xử lý. got là đầu ra thực tế từ ví dụ. test là bài kiểm tra có chứa example. out là hàm đầu ra được truyền cho DocTestRunner.run().

report_failure(out, test, example, got)

Báo cáo rằng ví dụ đã cho không thành công. Phương thức này được cung cấp để cho phép các lớp con của DocTestRunner tùy chỉnh đầu ra của chúng; nó không nên được gọi trực tiếp.

example là ví dụ sắp được xử lý. got là đầu ra thực tế từ ví dụ. test là bài kiểm tra có chứa example. out là hàm đầu ra được truyền cho DocTestRunner.run().

report_unexpected_exception(out, test, example, exc_info)

Báo cáo rằng ví dụ đã cho đưa ra một ngoại lệ không mong muốn. Phương thức này được cung cấp để cho phép các lớp con của DocTestRunner tùy chỉnh đầu ra của chúng; nó không nên được gọi trực tiếp.

example là ví dụ sắp được xử lý. exc_info là một bộ chứa thông tin về ngoại lệ không mong muốn (được trả về bởi sys.exc_info()). test là bài kiểm tra có chứa example. out là hàm đầu ra được truyền cho DocTestRunner.run().

run(test, compileflags=None, out=None, clear_globs=True)

Chạy các ví dụ trong test (đối tượng DocTest) và hiển thị kết quả bằng hàm ghi out. Trả về một phiên bản TestResults.

Các ví dụ được chạy trong không gian tên test.globs. Nếu clear_globs là true (mặc định), thì vùng tên này sẽ bị xóa sau khi chạy thử để hỗ trợ việc thu gom rác. Nếu bạn muốn kiểm tra không gian tên sau khi quá trình kiểm tra hoàn tất, hãy sử dụng clear_globs=False.

compileflags cung cấp bộ cờ mà trình biên dịch Python sẽ sử dụng khi chạy các ví dụ. Nếu không được chỉ định thì nó sẽ mặc định tập hợp các cờ nhập trong tương lai áp dụng cho globs.

Đầu ra của mỗi ví dụ được kiểm tra bằng trình kiểm tra đầu ra của DocTestRunner và kết quả được định dạng bằng phương thức DocTestRunner.report_*().

summarize(verbose=None)

In bản tóm tắt tất cả các trường hợp thử nghiệm đã được DocTestRunner này chạy và trả về một phiên bản TestResults.

Đối số verbose tùy chọn kiểm soát mức độ chi tiết của bản tóm tắt. Nếu mức độ chi tiết không được chỉ định thì mức độ chi tiết của DocTestRunner sẽ được sử dụng.

DocTestParser có các thuộc tính sau:

tries

Số lượng ví dụ đã thử.

failures

Số lượng ví dụ thất bại

skips

Số lượng ví dụ bị bỏ qua

Added in version 3.13.

Đối tượng OutputChecker

class doctest.OutputChecker

Một lớp được sử dụng để kiểm tra xem đầu ra thực tế từ một ví dụ doctest có khớp với đầu ra dự kiến hay không. OutputChecker xác định hai phương thức: check_output(), so sánh một cặp đầu ra nhất định và trả về True nếu chúng khớp; và output_difference(), trả về một chuỗi mô tả sự khác biệt giữa hai kết quả đầu ra.

OutputChecker định nghĩa các phương thức sau:

check_output(want, got, optionflags)

Trả về True nếu đầu ra thực tế từ một ví dụ (got) khớp với đầu ra dự kiến (want). Các chuỗi này luôn được coi là khớp nếu chúng giống hệt nhau; nhưng tùy thuộc vào cờ tùy chọn mà người chạy thử nghiệm đang sử dụng, cũng có thể có một số loại kết quả không chính xác. Xem phần Cờ tùy chọn để biết thêm thông tin về cờ tùy chọn.

output_difference(example, got, optionflags)

Trả về một chuỗi mô tả sự khác biệt giữa kết quả đầu ra dự kiến của một ví dụ nhất định (example) và kết quả đầu ra thực tế (got). optionflags là tập hợp các cờ tùy chọn được sử dụng để so sánh wantgot.

Gỡ lỗi

Doctest cung cấp một số cơ chế để gỡ lỗi các ví dụ doctest:

  • Một số hàm chuyển đổi doctest thành các chương trình Python có thể thực thi được, có thể chạy trong trình gỡ lỗi Python, pdb.

  • Lớp DebugRunner là lớp con của DocTestRunner đưa ra một ngoại lệ cho ví dụ bị lỗi đầu tiên, chứa thông tin về ví dụ đó. Thông tin này có thể được sử dụng để thực hiện gỡ lỗi sau khi thử nghiệm trên ví dụ.

  • Các trường hợp unittest được tạo bởi DocTestSuite() hỗ trợ phương thức debug() được xác định bởi unittest.TestCase.

  • Bạn có thể thêm lệnh gọi tới pdb.set_trace() trong một ví dụ tốt nhất và bạn sẽ truy cập trình gỡ lỗi Python khi dòng đó được thực thi. Sau đó, bạn có thể kiểm tra giá trị hiện tại của các biến, v.v. Ví dụ: giả sử a.py chỉ chứa chuỗi tài liệu mô-đun này

    """
    >>> định nghĩa f(x):
    ...g(x*2)
    >>> định nghĩa g(x):
    ... in(x+3)
    ... nhập pdb; pdb.set_trace()
    >>>f(3)
    9
    """
    

    Sau đó, phiên Python tương tác có thể trông như thế này:

    >>> nhập a, doctest
    >>> doctest.testmod(a)
    --Trở về--
    > <doctest a[1]>(3)g()->Không có
    -> nhập pdb; pdb.set_trace()
    (Pdb) danh sách
      1 độ phân giải g(x):
      2 bản in(x+3)
      3 -> nhập pdb; pdb.set_trace()
    [EOF]
    (Pdb) p x
    6
    (Pdb) bước
    --Trở về--
    > <doctest a[0]>(2)f()->Không có
    ->g(x*2)
    (Pdb) danh sách
      1 xác định f(x):
      2 -> g(x*2)
    [EOF]
    (Pdb) p x
    3
    (Pdb) bước
    --Trở về--
    > <doctest a[2]>(1)?()->Không có
    -> f(3)
    (Pdb) tiếp
    (0, 3)
    >>>
    

Các hàm chuyển đổi doctest thành mã Python và có thể chạy mã tổng hợp trong trình gỡ lỗi:

doctest.script_from_examples(s)

Chuyển đổi văn bản có ví dụ thành tập lệnh.

Đối số s là một chuỗi chứa các ví dụ tốt nhất. Chuỗi được chuyển đổi thành tập lệnh Python, trong đó các ví dụ doctest trong s được chuyển đổi thành mã thông thường và mọi thứ khác được chuyển đổi thành nhận xét Python. Tập lệnh được tạo sẽ được trả về dưới dạng chuỗi. Ví dụ::

nhập doctest
print(doctest.script_from_examples(r"""
    Đặt x và y thành 1 và 2.
    >>> x, y = 1, 2

    In tổng của chúng:
    >>> in(x+y)
    3
"""))

hiển thị:

# Set x và y thành 1 và 2.
x, y = 1, 2
#
# Print tổng của họ:
in(x+y)
# Expected:
## 3

Hàm này được các hàm khác sử dụng nội bộ (xem bên dưới), nhưng cũng có thể hữu ích khi bạn muốn chuyển đổi phiên Python tương tác thành tập lệnh Python.

doctest.testsource(module, name)

Chuyển đổi doctest của một đối tượng thành một tập lệnh.

Đối số module là một đối tượng mô-đun hoặc tên có dấu chấm của một mô-đun, chứa đối tượng mà các doctest được quan tâm. Đối số name là tên (trong mô-đun) của đối tượng có tài liệu quan tâm. Kết quả là một chuỗi chứa chuỗi tài liệu của đối tượng được chuyển đổi thành tập lệnh Python, như được mô tả cho script_from_examples() ở trên. Ví dụ: nếu mô-đun a.py chứa hàm cấp cao nhất f(), thì

nhập một, doctest
print(doctest.testsource(a, "a.f"))

in một phiên bản tập lệnh của chuỗi tài liệu của hàm f(), với các doctest được chuyển đổi thành mã và phần còn lại được đặt trong các nhận xét.

doctest.debug(module, name, pm=False)

Gỡ lỗi các tài liệu cho một đối tượng.

Các đối số modulename giống như đối với hàm testsource() ở trên. Tập lệnh Python tổng hợp cho chuỗi tài liệu của đối tượng được đặt tên được ghi vào một tệp tạm thời và sau đó tệp đó được chạy dưới sự kiểm soát của trình gỡ lỗi Python, pdb.

Một bản sao nông của module.__dict__ được sử dụng cho cả bối cảnh thực thi cục bộ và toàn cục.

Đối số tùy chọn pm kiểm soát xem có sử dụng tính năng gỡ lỗi sau khi chết hay không. Nếu pm có giá trị thực, tệp tập lệnh sẽ được chạy trực tiếp và trình gỡ lỗi chỉ được tham gia nếu tập lệnh kết thúc bằng cách đưa ra một ngoại lệ chưa được xử lý. Nếu đúng như vậy thì việc gỡ lỗi sau khi xử lý sẽ được thực hiện, thông qua pdb.post_mortem(), chuyển đối tượng truy nguyên từ ngoại lệ chưa được xử lý. Nếu pm không được chỉ định hoặc sai, tập lệnh sẽ được chạy trong trình gỡ lỗi ngay từ đầu, thông qua việc chuyển lệnh gọi exec() thích hợp tới pdb.run().

doctest.debug_src(src, pm=False, globs=None)

Gỡ lỗi các tài liệu trong một chuỗi.

Điều này giống như hàm debug() ở trên, ngoại trừ một chuỗi chứa các ví dụ doctest được chỉ định trực tiếp, thông qua đối số src.

Đối số tùy chọn pm có ý nghĩa tương tự như trong hàm debug() ở trên.

Đối số tùy chọn globs cung cấp một từ điển để sử dụng làm bối cảnh thực thi cục bộ và toàn cục. Nếu không được chỉ định hoặc None, một từ điển trống sẽ được sử dụng. Nếu được chỉ định, một bản sao nông của từ điển sẽ được sử dụng.

Lớp DebugRunner và các ngoại lệ đặc biệt mà nó có thể nêu ra, được các tác giả khung thử nghiệm quan tâm nhất và sẽ chỉ được phác thảo ở đây. Xem mã nguồn và đặc biệt là chuỗi doc của DebugRunner (là doctest!) để biết thêm chi tiết:

class doctest.DebugRunner(checker=None, verbose=None, optionflags=0)

Một lớp con của DocTestRunner đưa ra một ngoại lệ ngay khi gặp lỗi. Nếu một ngoại lệ không mong muốn xảy ra, một ngoại lệ UnexpectedException sẽ được đưa ra, chứa bài kiểm tra, ví dụ và ngoại lệ ban đầu. Nếu đầu ra không khớp thì ngoại lệ DocTestFailure sẽ được đưa ra, chứa thử nghiệm, ví dụ và đầu ra thực tế.

Để biết thông tin về các tham số và phương thức của hàm tạo, hãy xem tài liệu về DocTestRunner trong phần API nâng cao.

Có hai trường hợp ngoại lệ có thể được đưa ra bởi các phiên bản DebugRunner:

exception doctest.DocTestFailure(test, example, got)

Một ngoại lệ do DocTestRunner đưa ra để báo hiệu rằng kết quả thực tế của một ví dụ doctest không khớp với kết quả mong đợi của nó. Các đối số của hàm tạo được sử dụng để khởi tạo các thuộc tính có cùng tên.

DocTestFailure xác định các thuộc tính sau:

DocTestFailure.test

Đối tượng DocTest đang được chạy khi ví dụ này không thành công.

DocTestFailure.example

Zz000zz bị lỗi.

DocTestFailure.got

Đầu ra thực tế của ví dụ.

exception doctest.UnexpectedException(test, example, exc_info)

Một ngoại lệ do DocTestRunner đưa ra để báo hiệu rằng một ví dụ tốt nhất đã đưa ra một ngoại lệ không mong muốn. Các đối số của hàm tạo được sử dụng để khởi tạo các thuộc tính có cùng tên.

UnexpectedException xác định các thuộc tính sau:

UnexpectedException.test

Đối tượng DocTest đang được chạy khi ví dụ này không thành công.

UnexpectedException.example

Zz000zz bị lỗi.

UnexpectedException.exc_info

Một bộ chứa thông tin về ngoại lệ không mong muốn được trả về bởi sys.exc_info().

hộp xà phòng

Như đã đề cập trong phần giới thiệu, doctest đã phát triển với ba mục đích sử dụng chính:

  1. Kiểm tra các ví dụ trong tài liệu.

  2. Kiểm tra hồi quy.

  3. Tài liệu có thể thực thi/kiểm tra khả năng đọc viết.

Những mục đích sử dụng này có những yêu cầu khác nhau và điều quan trọng là phải phân biệt chúng. Đặc biệt, việc lấp đầy chuỗi tài liệu của bạn bằng các trường hợp kiểm thử khó hiểu sẽ tạo ra tài liệu tồi.

Khi viết một chuỗi tài liệu, hãy cẩn thận khi chọn các ví dụ về chuỗi tài liệu. Có một nghệ thuật trong việc này cần phải học --- ban đầu nó có thể không tự nhiên. Các ví dụ nên thêm giá trị đích thực vào tài liệu. Một ví dụ điển hình thường có giá trị bằng nhiều lời nói. Nếu được thực hiện cẩn thận, các ví dụ sẽ là vô giá đối với người dùng của bạn và sẽ mang lại lợi ích gấp nhiều lần cho thời gian cần thiết để thu thập chúng khi năm tháng trôi qua và mọi thứ thay đổi. Tôi vẫn ngạc nhiên về tần suất một trong các ví dụ doctest của tôi ngừng hoạt động sau một thay đổi "vô hại".

Doctest cũng tạo ra một công cụ tuyệt vời để kiểm tra hồi quy, đặc biệt nếu bạn không tiết kiệm văn bản giải thích. Bằng cách xen kẽ văn xuôi và ví dụ, việc theo dõi những gì thực sự đang được thử nghiệm và tại sao sẽ trở nên dễ dàng hơn nhiều. Khi thử nghiệm thất bại, văn xuôi tốt có thể giúp bạn dễ dàng tìm ra vấn đề là gì và cách khắc phục. Đúng là bạn có thể viết những bình luận sâu rộng trong thử nghiệm dựa trên mã, nhưng rất ít lập trình viên làm được điều đó. Nhiều người đã phát hiện ra rằng việc sử dụng các phương pháp doctest sẽ dẫn đến các bài kiểm tra rõ ràng hơn nhiều. Có lẽ điều này đơn giản là do doctest làm cho việc viết văn xuôi dễ hơn viết mã một chút, trong khi viết nhận xét bằng mã khó hơn một chút. Tôi nghĩ nó còn sâu xa hơn thế: thái độ tự nhiên khi viết một bài kiểm tra dựa trên doctest là bạn muốn giải thích những điểm hay của phần mềm và minh họa chúng bằng các ví dụ. Điều này tự nhiên dẫn đến các tệp thử nghiệm bắt đầu với các tính năng đơn giản nhất và tiến triển một cách hợp lý đến các trường hợp phức tạp và khó khăn. Kết quả là một câu chuyện mạch lạc, thay vì một tập hợp các chức năng riêng biệt kiểm tra các phần chức năng riêng biệt dường như ngẫu nhiên. Đó là một thái độ khác và tạo ra những kết quả khác, làm mờ đi sự khác biệt giữa kiểm tra và giải thích.

Kiểm tra hồi quy tốt nhất nên giới hạn ở các đối tượng hoặc tệp chuyên dụng. Có một số lựa chọn để tổ chức kiểm tra:

  • Viết các tệp văn bản chứa các trường hợp kiểm thử làm ví dụ tương tác và kiểm tra các tệp bằng testfile() hoặc DocFileSuite(). Điều này được khuyến khích, mặc dù đây là cách dễ thực hiện nhất đối với các dự án mới, được thiết kế ngay từ đầu để sử dụng doctest.

  • Xác định các hàm có tên _regrtest_topic bao gồm các chuỗi tài liệu đơn lẻ, chứa các trường hợp thử nghiệm cho các chủ đề được đặt tên. Các chức năng này có thể được bao gồm trong cùng một tệp với mô-đun hoặc được tách thành một tệp thử nghiệm riêng.

  • Xác định ánh xạ từ điển __test__ từ các chủ đề kiểm tra hồi quy đến các chuỗi tài liệu chứa các trường hợp kiểm tra.

Khi bạn đã đặt các bài kiểm thử của mình vào một mô-đun, chính mô-đun đó có thể là trình chạy thử nghiệm. Khi kiểm thử thất bại, bạn có thể sắp xếp để người chạy thử chỉ chạy lại doctest bị lỗi trong khi bạn gỡ lỗi. Đây là một ví dụ tối thiểu về một người chạy thử nghiệm như vậy:

nếu __name__ == '__main__':
    nhập doctest
    cờ = doctest.REPORT_NDIFF|doctest.FAIL_FAST
    nếu len(sys.argv) > 1:
        tên = sys.argv[1]
        nếu tên trong toàn cầu():
            obj = toàn cầu() [tên]
        khác:
            obj = __test__[tên]
        doctest.run_docstring_examples(obj, Globals(), name=name,
                                       optionflags=cờ)
    khác:
        thất bại, tổng cộng = doctest.testmod(optionflags=flags)
        print(f"{fail} lỗi trong tổng số {total} lần kiểm tra")

Chú thích cuối trang