def test_main(self): func = deal.post(lambda x: x > 0)(lambda x: -x) with self.subTest(text='good'): self.assertEqual(func(-4), 4) with self.subTest(text='error'): with self.assertRaises(deal.PostContractError): func(4)
def test_source_get_chain(): sum_contract = deal.chain( deal.pre(lambda a, b: a > 0), deal.pre(lambda a, b: b > 0), deal.post(lambda res: res > 0), ) @sum_contract def sum(a, b): return a + b with pytest.raises(deal.ContractError) as exc_info: sum(-2, 3) assert exc_info.value.source == 'a > 0' with pytest.raises(deal.ContractError) as exc_info: sum(2, -3) assert exc_info.value.source == 'b > 0' state.color = False assert str(exc_info.value) == 'expected b > 0 (where a=2, b=-3)' state.color = True
def test_return_value_fulfils_contract(self): func = deal.post(lambda x: x > 0)(lambda x: -x) assert func(-4) == 4 with pytest.raises(deal.PostContractError): func(4)
# built-in from typing import List # external import pytest # project import deal # if you have more than 2-3 contracts, # consider moving them from decorators into separate variable # like this: contract_for_index_of = deal.chain( # result is an index of items deal.post(lambda result: result >= 0), deal.ensure(lambda items, item, result: result < len(items)), # element at this position matches item deal.ensure( lambda items, item, result: items[result] == item, message='invalid match', ), # element at this position is the first match deal.ensure( lambda items, item, result: not any(el == item for el in items[:result]), message='not the first match', ), # LookupError will be raised if no elements found deal.raises(LookupError), deal.reason(LookupError, lambda items, item: item not in items), # no side-effects