drewverlee.github.io/harmony_through_testing/
harmony
when working in pure functional land, mocking is trivial. Dependencies are passed as parameters, no need to set up state, certainty of correctness based on test is absolute, because of referential transparency so certainty of correctness for the particular input value you choose.
im_a_global = 1 def im_a_function_that_creates_some_side_effect(x): im_a_global += 1 return x + 1 rv = im_a_function_that_creates_a_side_effect(5)
a side effect occurs any time we change state. A function is said to have side effects if these changes last outside the scope of the function.
Using pure functions can also allow some optimizations by changing evaluation order.The big win, however, stems from pure functions being conceptually simpler and much easier to test.
unitest
class TestFoo(unittest.TestCase): def test_foo(self): self.assertEqual(foo(), "lol")
pytest
def test_foo(): assert foo() == 'lol'
conftest file
from file_where_you_create_app import create_app def app(): return create_app("Development")
testfile
def test_app(client) # client is created for you assert client.get(url_for("a_vew")).status_code == 200
unit has some marks
@unittest.skipIf(mylib.__version__ < (1, 3), "not supported in this library version") def test_function(self):
pytest has some marks
import sys @pytest.mark.skipif(sys.version_info < (3,3), reason="requires python3.3") def test_function(): ...
pytest has custom markers
database_test = pytest.mark.DATABASE_TEST @database_test def test_some_part_of_db(): ...
import pytest @pytest.mark.parametrize("input,expected", [ ("3+5", 8), ("2+4", 6), ("6*9", 42), ]) def test_eval(input, expected): assert eval(input) == expected
def wet(a_list): a_list[0] = 1 a_list[1] = 2 a_list[2] = 3 return a_list def dry(a_list): for i, element in enumerate(a_list): a_list[i] = element += 1 return a_list
def wet_test_every_element_in_a_list(): result_list = wet(a_list) assert result_list[0] == 1 assert result_list[1] == 2 assert result_list[2] == 3 def dry_test_every_element_in_a_list(): result_list = dry(a_list) for i, expected_value in enumerate([1, 2, 3]): assert result_list[i] == expected_value
@pytest.fixture def some_setup(): config = "some" + " " + "setup" return config
def some_test(some_setup): assert some_setup == "some setup" def some_OTHER_test(some_setup): assert foo(some_setup) == "other setup"
@pytest.fixture(params=[" with peanut butter", " with mold"]) def fixture_for_fav_food(request): food = fav_food() add_on = request.param food += add_on def happens_after_test(): food_after = food.replace(add_on, "") request.addfinalizer(happens_after_test) return food
from fav_food import fav_food @pytest.fixture(params=[" with peanut butter", " with mold"]) def fixture_for_fav_food(request): food = fav_food() print("BEFORE {}".format(food)) add_on = request.param food += add_on print("WITH SETUP {}".format(food)) def happens_after_test(): food_after = food.replace(add_on, "") print("AFTER {}".format(food_after)) request.addfinalizer(happens_after_test) return food
▾ root_project_folder/ ▾ tests/ conftest.py <--- were fixtures live test_fav_food.py <--- test file fav_food.py <-- module
def fav_food(): return 'apples'
def test_fav_food(fixture_for_fav_food): assert fixture_for_fav_food == 'apples with peanut butter'
(testing_harmony)➜ parm_test git:(master) ✗ py.test -s
how far can we take it..
'a' +-----+ +-----+ | | | | +> <+ ab ac | \ abc\ abd
py - Beautiful is better than ugly. un - Explicit is better than implicit. py?- Simple is better than complex. ?? - Complex is better than complicated. py - Flat is better than nested. py - Sparse is better than dense. py - Readability counts. un - Special cases aren't special enough to break the rules. un - Although practicality beats purity. -- - Errors should never pass silently. -- - Unless explicitly silenced. -- - In the face of ambiguity, refuse the temptation to guess. un - There should be one-- and preferably only one --obvious way to do it. -- - Although that way may not be obvious at first unless you're Dutch. -- - Now is better than never. -- - Although never is often better than *right* now. -- - If the implementation is hard to explain, it's a bad idea. -- - If the implementation is easy to explain, it may be a good idea. -- - Namespaces are one honking great idea -- let's do more of those!