提问人:zaelcovsky 提问时间:10/4/2023 更新时间:10/4/2023 访问量:38
执行时间(性能)装饰器的单元测试
Unit tests for execution time (perfomance) decorator
问:
我有一个执行时间(性能)装饰器,它计算给定函数的平均执行时间并使用值打印结果:float
from time import perf_counter, sleep
def benchmark_decorator(func):
def wrapper(*args, **kwargs):
results = list()
n_repeats = 3
for i in range(n_repeats):
time_start = perf_counter()
result = func(*args, **kwargs)
time_end = perf_counter()
time_duration = time_end - time_start
results.append(time_duration)
print(f'>run {i+1} took {time_duration} seconds')
avg_duration = sum(results) / n_repeats
print(f'Took {avg_duration} seconds on average')
return result
return wrapper
如何测试此装饰器是否正确计算平均时间? 例如,让我们检查一下这个简单的函数:
@benchmark_decorator
def func(seconds):
sleep(seconds)
func(1)
我们有这样的输出:
>run 1 took 1.0005083000287414 seconds
>run 2 took 1.0000609999988228 seconds
>run 3 took 1.000284199952148 seconds
Took 1.0002844999932374 seconds on average
所以我每次得到的结果都略有不同,不能只用一个数字来断言这个结果。
此外,我不能只用 example,因为函数可以非常快,我们将得到非常低的时间值:.3f
@benchmark_decorator
def func():
pass
func()
给出以下输出:
>run 1 took 4.400033503770828e-06 seconds
>run 2 took 1.500011421740055e-06 seconds
>run 3 took 5.00003807246685e-07 seconds
Took 2.133349577585856e-06 seconds on average
如果我们用这个值四舍五入,我们将得到零。
那么如何正确地测试这个略有不同的时间呢?.3f
答:
1赞
Tzane
10/4/2023
#1
如果您使用的是 ,则可以使用 capsys
夹具捕获 stdout,从中解析平均结果,并在测试中执行舍入,如下所示pytest
import pytest
@benchmark_decorator
def wrap_sleep(seconds):
sleep(seconds)
def test_benchmark_decorator(capsys):
wrap_sleep(1)
captured = capsys.readouterr()
average = float(captured.out.rsplit(" ", 4)[1])
# Rounding
assert average == pytest.approx(1, abs=1e-3)
# Relative 2% error
assert average == pytest.approx(1, rel=0.02)
评论
0赞
zaelcovsky
10/4/2023
感谢您的回复,但就像我上面提到的 - 四舍五入不是好的做法,因为我们可以得到非常不同的时间值(取决于函数),无论是非常小的还是非常大的,我们不能在任何地方使用。abs=1e-3
1赞
Tzane
10/4/2023
如果你想检查你用这个装饰器测试的每个函数的性能,我认为你需要为每个测试单独参数化预期的性能。如果舍入不适用于您的用例,您也可以使用相对近似值。pytest.approx
评论