使用參數化功能為Python軟件包編寫非常簡潔的單元測試的指南。
第一部分將說明隨附軟件包的結構以及如何在您的系統上進行設置。 如果您只想查看PyTest中的參數化示例,則可以直接跳至第二部分"參數化測試"。
包裝結構和設置
克隆的存儲庫將包含一個名為efficiency_dedup的包和一個包含測試用例的tests文件夾。
該軟件包旨在在Python列表中執行有效的重複數據刪除(刪除重複值)。 它分兩個步驟執行此操作-首先,對列表進行排序,然後使用一種重複數據刪除算法對已排序的列表進行重複數據刪除。 請注意,此軟件包僅用於說明; 因此,許多代碼在功能上是多餘的。 這是包的結構:
efficient_dedup
|
| - efficient_deduplication
| - deduplication_algorithms
| - sorting_algorithms
|
| - tests
|
| - test_efficient_deduplication
它基於策略設計模式。 高效重複數據刪除模塊中定義的EfficientDeduplicator類是上下文:
<code>from .deduplication_algorithms import DeduplicationAlgorithm from .sorting_algorithms import SortingAlgorithmclass
EfficientDeduplicator
:def
__init__
(
self
,sorter:
SortingAlgorithm,deduplicator:
DeduplicationAlgorithm):self
.__sorter = sorting_algorithmself
.__deduplicator = deduplication_algorithmdef
deduplicate_efficiently
(
self
, list_to_deduplicate):return
self
.__deduplicator.deduplicate(self
.__sorter.sort(list_to_deduplicate))/<code>
要求每個deduplication_algorithms和sorting_algorithms模塊都實施策略。
在sorting_algorithms模塊中僅定義了兩種策略行為,它們都封裝在一個類SortingAlgorithm中。 您可以通過將反向參數設置為True或False來選擇所需的特定行為:
<code>
class
SortingAlgorithm
:def
__init__
(
self
, reverse=False):self
.__reverse = reversedef
sort
(
self
, list_to_sort):if
self
.__reverse:
list_to_sort.sort(reverse=True)else:
list_to_sort.sort()return
list_to_sort/<code>
deduplication_algorithms模塊包含四個不同的實現,每個實現封裝在一個不同的類中。 第一個類DeduplicationAlgorithm充當其他三個實現的基類(這不是實際的代碼,因為此處省略了函數的定義):
<code>class
DeduplicationAlgorithm
:def
deduplicate
(
self
, list_with_duplicates):return
class
HashSetDeduplicationAlgorithm
(DeduplicationAlgorithm
):def
deduplicate
(
self
, list_with_duplicates):return
class
CustomComparisonDeduplicationAlgorithm
(DeduplicationAlgorithm
):def
__init__
(
self
, comparison_fun=lambda x,y:
x == y):self
.__comparison_fun = comparison_fundef
deduplicate
(
self
, list_with_duplicates):return
class
LookaheadCustomComparisonDeduplicationAlgorithm
(DeduplicationAlgorithm
):def
__init__
(
self
, lookahead_window_size=100
, comparison_fun=lambda x,y:
x == y):self
.__lookahead_window_size = lookahead_window_sizeself
.__comparison_fun = comparison_fundef
deduplicate
(
self
, list_with_duplicates):return
/<code>
要運行測試用例,將cd放入軟件包的根文件夾並運行以下命令:
> cd /efficient_dedup> python -m pytest
參數化測試
因此,在繼續操作之前,讓我們先進行總結。 我們有一個稱為EfficientDeduplicator的上下文類,它需要兩種策略:SortingAlgorithm和DeduplicationAlgorithm。 該上下文類僅具有一個功能,即deduplicate_效率地。
我們希望使用策略的所有可能組合以及一些不同的測試數據來高效地測試重複數據刪除。 正如我們將看到的,如果使用參數化,則可以在一個測試用例中完成所有這些工作。
數據參數化
這是我們可以編寫的最基本的測試用例:
<code>def
test_deduplicate_efficiently(self):
sorter
=
SortingAlgorithm(
reverse=False
)
deduplicator
=
LookaheadCustomComparisonDeduplicationAlgorithm(
lookahead_window_size=100,
comparison_fun=lambda
x,
y:
x
==
y
)
efficient_deduplicator
=
EfficientDeduplicator(sorter,
deduplicator)
list_with_duplicates
=
[1,
2
,
2
,
7
,
4
,
8
,
9
,
3
,
5
,
4
,
6
,
3
,
1
,
7
,
3
,
3
]
assert
set(efficient_deduplicator.deduplicate_efficiently(list_with_duplicates))
==
set(list_with_duplicates)
/<code>
假設我們要使用幾種可能的輸入(即,變量list_with_duplicates的幾種不同值)來執行此測試。 首先,我們在測試函數中使list_with_duplicates為參數。 然後,我們通過@ pytest.marker.parametrize裝飾器提供該參數的值列表。
<code>@pytest.mark.parametrize('list_with_duplicates',
[
[1,
2
,
2
,
7
,
4
,
8
,
9
,
3
,
5
,
4
,
6
,
3
,
1
,
7
,
3
,
3
],
[1,
2
,
3
,
5
,
7
,
9
,
4
,
7
,
8
],
[1,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
]
])
def
test_deduplicate_efficiently_w_data_parametrization(self,
list_with_duplicates):
sorter
=
SortingAlgorithm()
deduplicator
=
DeduplicationAlgorithm()
efficient_deduplicator
=
EfficientDeduplicator(sorter,
deduplicator)
assert
set(efficient_deduplicator.deduplicate_efficiently(list_with_duplicates))
==
set(list_with_duplicates)
/<code>
這是在Pytest中進行參數化的一般過程:
· 您可以在測試函數的參數列表中指定要參數化的變量
· 然後,使用@ pytest.marker.parametrize裝飾器提供變量值的列表。
類的參數化
現在,假設我們想對DeduplicationAlgorithm的所有可能的不同實現運行基本測試,但要保持數據不變。 這可以通過Pytest參數化,Python的inspect模塊以及類在Python中是一等公民的巧妙組合來實現(這意味著它們可以作為函數的參數傳遞)。
要獲取deduplication_algorithms模塊中所有類的列表,我們可以導入該模塊,然後運行以下代碼:
<code>print([
name)
for
name, obj
in
inspect.getmembers(deduplication_algorithms)
if
inspect.isclass(obj)
])
/<code>
這為我們提供了模塊中的類的列表:
[, ,,]
然後我們可以將其傳遞給@ pytest.marker.parametrize裝飾器:
<code>@pytest.mark.parametrize('deduplication_algorithm_class',
[
getattr(deduplication_algorithms,
name)
for
name,
obj
in
inspect.getmembers(deduplication_algorithms)
if
inspect.isclass(obj)
])
def
test_deduplicate_efficiently_w_class_parametrization(self,
deduplication_algorithm_class):
sorter
=
SortingAlgorithm()
deduplicator
=
deduplication_algorithm_class()
efficient_deduplicator
=
EfficientDeduplicator(sorter,
deduplicator)
list_with_duplicates
=
[1,
2
,
2
,
7
,
4
,
8
,
9
,
3
,
5
,
4
,
6
,
3
,
1
,
7
,
3
,
3
]
assert
set(efficient_deduplicator.deduplicate_efficiently(list_with_duplicates))
==
set(list_with_duplicates)
/<code>
等等。 您已經在Pytest中實現了類參數化!
結合數據和類參數化
Pytest的一個很棒的功能是,如果在一個測試函數上堆疊許多@ pytest.marker.parametrize裝飾器,它將自動為所有裝飾器(如固定產品)上的所有可能組合運行測試。 最後,我們將使用此功能在單個測試用例中結合上述兩個參數; 實際上,我們還將對SortingAlgorithm的reverse屬性進行參數化:
<code>@pytest.mark.parametrize('list_with_duplicates',
[
[1,
2
,
2
,
7
,
4
,
8
,
9
,
3
,
5
,
4
,
6
,
3
,
1
,
7
,
3
,
3
],
[1,
2
,
3
,
5
,
7
,
9
,
4
,
7
,
8
],
[1,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
]
])
@pytest.mark.parametrize('sorting_algorithm_reverse_option',
[
True
,
False
])
@pytest.mark.parametrize('deduplication_algorithm_class',
[
getattr(deduplication_algorithms,
name)
for
name,
obj
in
inspect.getmembers(deduplication_algorithms)
if
inspect.isclass(obj)
])
def
test_deduplicate_efficiently_w_data_and_class_parametrization(self,
list_with_duplicates,
sorting_algorithm_reverse_option,
deduplication_algorithm_class):
sorter
=
SortingAlgorithm(reverse=sorting_algorithm_reverse_option)
deduplicator
=
deduplication_algorithm_class()
efficient_deduplicator
=
EfficientDeduplicator(sorter,
deduplicator)
assert
set(efficient_deduplicator.deduplicate_efficiently(list_with_duplicates))
==
set(list_with_duplicates)
/<code>
這是您要克隆的軟件包中測試用例的最終形式。 如果您詳細運行Pytest:
> python -m pytest -v
您將觀察到這樣的輸出:
tests/test_efficient_deduplication.py::TestEfficientDeduplicators::test_deduplicate_efficiently[DeduplicationAlgorithm-True-list_with_duplicates0]
PASSED [33%]
上面輸出中的第二行列出了測試用例中使用的參數的確切組合。
結論
使用本文中介紹的技術,將來可以編寫更簡潔有效的Python測試用例!
要克隆本文隨附的源代碼,請私信譯者。
(本文翻譯自Rishabh Malviya的文章《PyTest: An Introduction to Parametrization》,參考:https://levelup.gitconnected.com/pytest-an-introduction-to-parametrization-2b27bebb6d4b)
關鍵字: deduplicate deduplicator def