Skip to content

unittesting_unit_examples

Yoseph Maguire edited this page Jan 16, 2020 · 2 revisions

Various test unit examples

This article outlines simple examples of various test units along with their unit syntax, as well as advice on when to use which unit.

Function - .<function name>()

This simple unit should be the go-to approach for testing functions.

Code Example

def foo():
    return "bar"

Test Example

@pytest.mark.describe(".foo()")
class TestFoo(object):
    @pytest.mark.it("Returns 'bar'")
    def test_returns_bar(self):
        assert foo() == "bar"

Function called under a certain condition - .<function name>() -- <condition>

This should only be used if there are sufficiently many differences in behavior of the function given a specific condition. For most situations, a regular function unit is more appropriate - only use this pattern if the differences are sufficiently large that they are, fundamentally, different units.

Code Example

def foo(mode):
    if mode == "mode1":
        do_thing1()
        do_thing2()
    elif mode == "mode2":
        do_thing3()
        do_thing4()

Test Example

@pytest.mark.describe(".foo() -- Using mode1")
class TestFooMode1(object):
    @pytest.mark.it("Does thing1")
    def test_thing_1(self, thing1):
        foo("mode1")
        assert thing1.done == True

    @pytest.mark.it("Does thing2")
    def test_thing_2(self, thing2):
        foo("mode1")
        assert thing2.done == True

@pytest.mark.describe(".foo() -- Using mode2")
class TestFooMode2(object):
    @pytest.mark.it("Does thing3")
    def test_thing_3(self, thing3):
        foo("mode2")
        assert thing3.done == True

    @pytest.mark.it("Does thing4")
    def test_thing_4(self, thing4):
        foo("mode2")
        assert thing4.done == True

Class - <class name>

This test unit should be applied in order to test various aspects of an instance of a class.

Beware: This unit should ONLY be applied to items that are semantically tightly coupled to an instance of a class. Methods that "do" something would be part of a separate unit. Generally this would apply only to dunder methods, instance attributes, etc.

Code Example

class Foo(object):

    def __init__(self):
        self._value = "buzz"

    def __repr__(self):
        return "foo"

    @property
    def value(self):
        return self._value

Test Example

@pytest.mark.describe("Foo")
class TestFoo(object):
    @pytest.mark.it("Instantiates with the .value attribute set to 'buzz'")
    def test_set_value_on_instantitation(self):
        f = Foo()
        assert f.value == "buzz"

    @pytest.mark.it("Maintains the .value attribute as a read-only property")
    def test_modify_value(self):
        f = Foo()
        with pytest.raises(AttributeError):
            f.value = "newval"

    @pytest.mark.it("Returns 'foo' as a string representation of an instance")
    def test_str_rep(self):
        f = Foo()
        assert str(f) == "foo"

Specific aspect of a class - <class name> - <aspect>

Sometimes, it may be convenient to split up the aspects encompassed by the unit above in order to more narrowly define a unit (especially if the only units that would be covered by a class unit all belong to a specific aspect - e.g instantiation)

Code Example

class Foo(object):

    def __init__(self, v):
        self.bar = "bar"
        self.buzz = "buzz"
        self.value = v

Test Example

@pytest.mark.describe("Foo - Instantiation")
class TestFooInstantiation(object):
    @pytest.mark.it("Sets the .bar attribute to 'bar'")
    def test_bar(self):
        f = Foo("val")
        assert f.bar == "bar"

    @pytest.mark.it("Sets the .buzz attribute to 'buzz'")
    def test_buzz(self):
        f = Foo("val")
        assert f.buzz == "buzz"

    @pytest.mark.it("Sets the .value attribute to the value of the parameter v")
    def test_value(self):
        v = "val"
        f = Foo(v)
        assert f.value == v

Class response to an external event - <class name> - OCCURANCE: <event name>

This unit is used in order to cover any handler methods of a class that respond to an externally triggered event.

Code Example

class Foo(object):
    def __init__(self):
        self.httpclient = HttpClient()
        self.httpclient.on_request_received = self._handle_request
        self.request_counter = 0

    def _handle_request(self, request):
        self.request_counter += 1
        request.is_completed = True

Test Example

@pytest.mark.describe("Foo - OCCURANCE: HTTP Request Received")
class TestFooHttpRequestReceived(object):
    @pytest.mark.it("Increments the request counter")
    def test_increments_counter(self):
        f = Foo()
        assert f.request_counter == 0

        f.httpclient.on_request_received(Request())

        assert f.request_counter == 1

    @pytest.mark.it("Marks the request object as completed")
    def test_marks_request_completed(self):
        f = Foo()
        r = Request()
        assert not r.is_completed

        f.httpclient.on_request_received(r)

        assert r.is_completed

Method in a class - <class name> - .<method name>()

This simple unit should be the go-to approach for testing methods on a class or instance of a class.

Code Example

class Foo(object):
    def buzz(self):
        return "buzz"

Test Example

@pytest.mark.describe("Foo - .buzz()")
class TestFooBuzz(object):
    @pytest.mark.it("Returns 'buzz'")
    def returns_buzz(self):
        f = Foo()
        assert f.buzz() == "buzz"

Method in a class called under a cerain condition - <class name> - .<method name>() -- <condition>

This should only be used if there are sufficiently many differences in behavior of the method given a specific condition. For most situations, a regular method unit is more appropriate - only use this pattern if the differences are sufficiently large that they are, fundamentally, different units.

Code Example

class Foo(object):
    def bar(mode):
        if mode == "mode1":
            do_thing1()
            do_thing2()
        elif mode == "mode2":
            do_thing3()
            do_thing4()

Test Example

@pytest.mark.describe("Foo - .bar() -- Using mode1")
class TestFooBarMode1(object):
    @pytest.mark.it("Does thing1")
    def test_thing_1(self, thing1):
        foo = Foo()
        foo.bar("mode1")
        assert thing1.done == True

    @pytest.mark.it("Does thing2")
    def test_thing_2(self, thing2):
        foo = Foo()
        foo.bar("mode1")
        assert thing2.done == True

@pytest.mark.describe("Foo - .bar() -- Using mode2")
class TestFooBarMode2(object):
    @pytest.mark.it("Does thing3")
    def test_thing_3(self, thing3):
        foo = Foo()
        foo.bar("mode2")
        assert thing3.done == True

    @pytest.mark.it("Does thing4")
    def test_thing_4(self, thing4):
        foo = Foo()
        foo.bar("mode2")
        assert thing4.done == True