Я пишу тесты для статистического анализа гипотез. Гипотеза привела меня к ZeroDivisionError
в моем коде, когда ему передаются очень разреженные данные. Поэтому я адаптировал свой код для обработки исключения; в моем случае это означает регистрацию причины и повторное создание исключения.
try:
val = calc(data)
except ZeroDivisionError:
logger.error(f"check data: {data}, too sparse")
raise
Мне нужно передать исключение через стек вызовов, потому что вызывающий абонент верхнего уровня должен знать, что произошло исключение, чтобы он мог передать код ошибки внешнему вызывающему абоненту (запрос REST API).
Изменить: я также не могу назначить разумное значение для val
; по сути, мне нужна гистограмма, и это происходит, когда я вычисляю разумную ширину ячейки на основе данных. Очевидно, что это не работает, когда данные немногочисленны. А без гистограммы алгоритм не может двигаться дальше.
Теперь моя проблема в том, что в моем тесте я делаю что-то вроде этого:
@given(dataframe)
def test_my_calc(df):
# code that executes the above code path
hypothesis
продолжает генерировать примеры сбоев, которые запускают ZeroDivisionError
, и я не знаю, как игнорировать это исключение. Обычно я бы отмечал такой тест с помощью pytest.mark.xfail(raises=ZeroDivisionError)
, но здесь я не могу этого сделать, поскольку тот же тест проходит для хорошо настроенных входов.
Что-то вроде этого было бы идеально:
- продолжайте тест, как обычно для большинства входных данных, однако
- когда поднят
ZeroDivisionError
, пропустите это как ожидаемый сбой.
Как я мог этого добиться? Нужно ли мне также помещать try: ... except: ...
в тестовое тело? Что мне нужно сделать в блоке except, чтобы отметить его как ожидаемый сбой?
Изменить: чтобы ответить на комментарий @hoefling, идеальным решением было бы разделение неудачных случаев. Но, к сожалению, hypothesis
не дает мне достаточно средств, чтобы это контролировать. В лучшем случае я могу контролировать общее количество и пределы (минимум, максимум) сгенерированных данных. Однако случаи неудачи имеют очень узкий разброс. Я не могу это контролировать. Я полагаю, что это суть гипотезы, и, возможно, мне вообще не следует использовать гипотезу для этого.
Вот как я генерирую свои данные (немного упрощенно):
cities = [f"city{i}" for i in range(4)]
cats = [f"cat{i}" for i in range(4)]
@st.composite
def dataframe(draw):
data_st = st.floats(min_value=0.01, max_value=50)
df = []
for city, cat in product(cities, cats):
cols = [
column("city", elements=st.just(city)),
column("category", elements=st.just(cat)),
column("metric", elements=data_st, fill=st.nothing()),
]
_df = draw(data_frames(cols, index=range_indexes(min_size=2)))
# my attempt to control the spread
assume(np.var(_df["metric"]) >= 0.01)
df += [_df]
df = pd.concat(df, axis=0).set_index(["city", "category"])
return df