6
\$\begingroup\$

I have some doubt when I should create an object in an Unit Test when the object needs to be stopped cleanly.

Usecase: I have a class ComplexClass which has 3 functions:

  • Start(): starts a Thread
  • Stop(): stops the thread
  • GetX(): which gets a value

When I test this class I want to check if GetX() returns 42, but first I have to start the thread, and after it I want to stop the Thread. (During testing I don't want many dangling threads running).

Are fixtures the way to do this?:

class TestComplexClass(object):
    @pytest.fixture()
    def complexclass(self, request):
        print ("[setup] ComplexClass")

        cc = ComplexClass()
        def fin():
            print ("[teardown] ComplexClass")
            cc.stop()
        request.addfinalizer(fin)
        return cc

    def test_GetX(self, complexclass):
        // do some stuff

        complexclass.start()
        assert complexclass.GetX() == 42, "GetX() should return 42"

Or should I create the object in the Unit Test?:

class TestComplexClass(object):
    def test_GetX(self, complexclass):
        // do some stuff

        cc = ComplexClass()
        try:
            cc.start()
            assert cc.GetX() == 42, "GetX() should return 42"
        except AssertionError:
            cc.stop
            raise

(Note I'm using Py.Test)

\$\endgroup\$
5
\$\begingroup\$

This somewhat depends on which principle you value higher. Small repetition:

F.I.R.S.T

F: Fast

Pretty self-explaining. Unit tests have to be as fast as possible for TDD to be viable.

I: Independent

Unit tests are not depending on anything else than the testing-framework and the class under test (cut). Not even on other tests. Unit tests prepare their own data and setup and clean up after themselves after evaluation. It must not matter in which order unit tests are executed!

R: Repeatable

Unit tests give the same result when they are repeatedly executed. Even on a new moon with low tide. They always look for the same results and perform the same operations.

S: Self-Validating

Unit tests validate the result of any operation themselves. They may not give any results to external classes for validation. Also they should be self-explaining as to why that result is expected.

T: Timely

Unit tests are to be written as close as possible before the implementation of a feature

principles freely adapted from Agile in a Flash


In this case you will have to evaluate the fast principle against the independent principle It will definitely be faster to create the instance of ComplexClass only once (on fixture setup), but that will undermine the independency of unit tests and may will cause problems when you depend on execution order.

\$\endgroup\$
4
  • \$\begingroup\$ Thanks @Vogel612, but why will it cause problems on execution order? Each Unit Test will be run after each other, so the cleanup will be done correctly, right? \$\endgroup\$
    – RvdK
    Mar 19 '14 at 7:03
  • \$\begingroup\$ @RvdK I don't really understand what you want to say. What I mean is, that if you depend on the execution order of tests, to get your results, you are doing something wrong. Most testing frameworks just take the tests as they come. Some may specify a position in testing, but that is against the "Spirit" of unit-testing. SetUp and TearDown of every test suite will be executed, and if the world ends and hell freezes over.. \$\endgroup\$
    – Vogel612
    Mar 19 '14 at 7:53
  • \$\begingroup\$ Ah this will not be the case. The term 'Unit Test' is a bit flawed here what I want to achieve. We are testing a component which detects devices in a thread. I want to use Py.Test to achieve this. In each test case the same ComplexClass will be created and destroyed and test a different functionality. \$\endgroup\$
    – RvdK
    Mar 19 '14 at 8:34
  • \$\begingroup\$ @RvdK why not reuse the ComplexClass for the whole [Fixture] then? \$\endgroup\$
    – Vogel612
    Mar 19 '14 at 12:05
2
\$\begingroup\$

Yes fixture is a simple way to do it. I would add the start there too, to make ComplexClass ready to go and reduce code replication.

The second example will not stop if the test succeed. Use finally instead of except. But, instead of duplicating the code each time, I'll use a contextmanager like this:

from contextlib import contextmanager
@contextmanager
def stoping_ComplexClass():
    try:
        cc = ComplexClass()
        cc.start()
        yield cc
    finally:
        cc.stop()

class TestComplexClass(object):
    def test_GetX(self, complexclass):
        # do stuff
        with stoping_ComplexClass() as cc:
            assert cc.GetX() == 42, "GetX() should return 42"

I'll use fixture when the initialization of ComplexClass is simple and not changing much with each test. But if you need to change the params every test, I think it will be easier to use a contextmanager.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.