Skip to content

Instantly share code, notes, and snippets.

@a-recknagel
Last active October 25, 2021 11:26
Show Gist options
  • Save a-recknagel/00ab259d1ee46326d27122b02f52cf20 to your computer and use it in GitHub Desktop.
Save a-recknagel/00ab259d1ee46326d27122b02f52cf20 to your computer and use it in GitHub Desktop.
bad test - good test
# adding widgets to my app
import my_app
def foo(user_id: int, num_widgets: int):
user = my_app.get_user(user_id)
widgets = []
for _ in range(num_widgets):
widgets.append(my_app.create_widget(user))
my_app.bootstrap(widgets)
return my_app.ensure_status() == 0
# testing that "it works correctly"
import pytest
@pytest.mark.parametrize(
"state, expected",
[
pytest.param(0, True, id="success"),
pytest.param(1, False, id="failure")
]
)
def test_foo(mocker, state, expected):
# arrange, if this block is big -> danger zone:
user_id, num_widgets = 1, 5
get_user = mocker.patch("my_app.get_user")
create_widget = mocker.patch("my_app.create_widget")
bootstrap = mocker.patch("my_app.bootstrap")
mocker.patch("my_app.ensure_status", return_value=state)
# act:
result = foo(user_id, num_widgets)
# assert, pretty much runs down the implementation line by line. ugly and high-maintenance:
get_user.assert_called_with(user_id) # trash
create_widget.assert_called_with(get_user.return_value) # trash
bootstrap.called_once() # trash
assert len(load_env.return_value) == num_widgets # maybe reasonable
assert result == expected # reasonable
def levenshtein(s1: str, s2: str) -> int:
"""Compute Levenshtein's edit distance between two strings.
The edit distance is defined as the number of change-operations
necessary to transform one string into another. Valid
operations are 1) adding a character, 2) deleting a character,
and 3) replacing a character. An edit distance of 0 means that
the input strings are equal, an edit distance of the length of
the longer strings means that they are as different as
possible.
For additional details, see
https://en.wikipedia.org/wiki/Levenshtein_distance
Raises:
ValueError if one of the inputs is not a string.
"""
if not isinstance(s1, str) or not isinstance(s1, str):
raise ValueError("Right types, please.")
if len(s1) < len(s2):
return levenshtein(s2, s1)
if len(s2) == 0:
return len(s1)
previous_row: List[int] = [*range(len(s2) + 1)]
for i, c1 in enumerate(s1):
current_row = [i + 1]
for j, c2 in enumerate(s2):
insertions = previous_row[j + 1] + 1
deletions = current_row[j] + 1
substitutions = previous_row[j] + (c1 != c2)
current_row.append(min(insertions, deletions, substitutions))
previous_row = current_row
return previous_row[-1]
# I could write these tests simply by reading the function-under-test's docstring,
# no need to take a look at the implementation.
import pytest
@pytest.mark.parametrize(
"s_1, s_2, distance",
[
("", "", 0),
("foo", "", 3),
("", "bar", 3),
("bar", "baz", 1),
("foofoofoo", "foo", 6),
],
)
def test_levenshtein(s_1, s_2, distance):
# arrange is done completely via parametrize
# act and assert:
assert levenshtein(s_1, s_2) == distance # perfectly reasonable
@pytest.mark.parametrize(
"s_1, s_2",
[
("", b""),
("foo", 3),
(1, 11),
],
)
def test_levenshtein_bad_types():
with pytest.raises(ValueError):
assert levenshtein(s_1, s_2) # reasonable
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment