Velkan
Velkan

Reputation: 7592

How to freely combine multiple setUp()/tearDown() base classes while using unittest?

There are various setups: creating an app driver, creating a dummy device, doing a login.

Some test cases need only app-driver, some need app-driver and login, some need app-driver and device, others use all three.

How to make something like a Combine<Ts...> template:

#include <iostream>

// The unittest.TestCase.
struct TestCase
{
    virtual void setUp() {}
    virtual void tearDown() {}
};

template <typename ...Ts>
struct Combine: public TestCase, public Ts...
{
    virtual void setUp() override { int t[] = { 0, (Ts::setUp(), 1)... }; }

    // TODO: invert the order
    virtual void tearDown() override { int t[] = { 0, (Ts::tearDown(), 1)... }; }
};

// Setups for 'login' only.
struct Login
{
    void setUp() { std::cout << "Login setup" << std::endl; }
    void tearDown() { std::cout << "Login teardown" << std::endl; }
};

// Setups for 'device' only.
struct Device
{
    void setUp() { std::cout << "Device setup" << std::endl; }
    void tearDown() { std::cout << "Device teardown" << std::endl; }
};

// A concrete test case.
struct MyTest: public Combine<Login, Device>
{
    void test() { std::cout << "test" << std::endl; }
};

int main()
{
    MyTest cd;
    cd.setUp();
    cd.test();
    cd.tearDown();
    Combine<Device> d;
    d.setUp();
    d.tearDown();
    return 0;
}

Output:

Login setup
Device setup
test
Login teardown
Device teardown
Device setup
Device teardown

Upvotes: 1

Views: 240

Answers (1)

bruno desthuilliers
bruno desthuilliers

Reputation: 77912

Use mixins, multiple inheritance and super() calls:

import unittest

class MixinBase(object):
    def setUp(self):
        pass

    def tearDown(self):
        pass


class LoginMixin(MixinBase):
    def setUp(self):
        print("LoginMixin.setUp()")
        self.login = "login"
        super(LoginMixin, self).setUp()

    def tearDown(self):
        super(LoginMixin, self).tearDown()
        print("LoginMixin.tearDown()")
        del self.login


class DeviceMixin(MixinBase):
    def setUp(self):
        print("DeviceMixin.setUp()")
        self.device = "device"
        super(DeviceMixin, self).setUp()

    def tearDown(self):
        super(DeviceMixin, self).tearDown()
        print("DeviceMixin.tearDown()")
        del self.device



class TestThatOnlyNeedsLogin(LoginMixin, unittest.TestCase):
    def test_something(self):
        self.assertEqual(self.login, "login")


class TestThatNeedsLoginAndDevice(LoginMixin, DeviceMixin, unittest.TestCase):
    def test_something(self):
        self.assertEqual(self.login, "login")
        self.assertEqual(self.device, "device")

# XXX actually useless (multiple inheritance works fine)
# but since you asked for it
def combinemixins(cls, *otherbases):
    bases = (cls,)  + otherbases
    Combined = type.__new__(type, "Combined", bases, {})
    return Combined

LoginAndDevice = combinemixins(LoginMixin, DeviceMixin)

class TestWithCombined(LoginAndDevice, unittest.TestCase):
    def test_something(self):
        self.assertEqual(self.login, "login")
        self.assertEqual(self.device, "device")


if __name__ == "__main__":
    unittest.main()

Upvotes: 2

Related Questions