Search code examples
unit-testingkotlinspek

Create an Abstract Spek for initializing shared objects with Kotlin and Spek


I'm trying to write unit tests with Kotlin + Spek framework. So far looks pretty good, but I have a problem that i have to write a lot of boilerplate code, if i want to have several Spec objects for one class i'm testing.

That's my code to illustrate the problem:

object MyFirstSpec : Spek({
    val myMock1: SomeObject1 = mock()
    val myMock2: SomeObject2 = mock()
    val myMock3: SomeObject3 = mock()
    val myMock4: SomeObject4 = mock()
    val myMock5: SomeObject5 = mock()
    val myMock6: SomeObject6 = mock()

    val myClass = MyClassToTest(myMock1, myMock2, myMock3, myMock4, myMock5, myMock6)

    given("something to test here") {
        //my test here
    }
})

object MyOtherSpec : Spek({
    val myMock1: SomeObject1 = mock()
    val myMock2: SomeObject2 = mock()
    val myMock3: SomeObject3 = mock()
    val myMock4: SomeObject4 = mock()
    val myMock5: SomeObject5 = mock()
    val myMock6: SomeObject6 = mock()

    val myClass = MyClassToTest(myMock1, myMock2, myMock3, myMock4, myMock5, myMock6)

    given("something else to test here") {
        //  my test here
    }
})

I would love to create the 'abstract' Spek, where I will define my mocks etc, and after that inherit or share it somehow across my Specs.

Is there a way to achieve that?


Solution

  • Unfortunately, I didn't find the way to make the Abstract Spek or inheritance, but I've found the way to use Fixtures to achieve the desired results.

    Here's my pseudo code demonstrating it:

    object MySpec: Spek({
        describe("my tests with Fixtures") {
            val myMock1: SomeObject1 = mock()
            val myMock2: SomeObject2 = mock()
            val myMock3: SomeObject3 = mock()
            val myMock4: SomeObject4 = mock()
            val myMock5: SomeObject5 = mock()
            val myMock6: SomeObject6 = mock()
    
            val myClass = MyClassToTest(myMock1, myMock2, myMock3, myMock4, myMock5, myMock6)
    
            afterEachTest { 
                reset(myMock1, myMock2, myMock3, myMock4, myMock5, myMock6)
                //or we can just clear invocations, if we don't want to reset the mocks
                //clearInvocations(myMock1, myMock2, myMock3, myMock4, myMock5, myMock6)
            }
    
            context("my first context for testing") {
                //initialization logic here specific for this context
                val somethingSpecificHere = MySpecificLogicObject()
                beforeEachTest { 
                    whenever(myMock1.doSomething()).thenReturn(someResult)
                }
                on ("calling the method i'm testing"){
                    myClass.myMethod(somethingSpecificHere)
    
                    it ("should do what expected") {
                        //verifies go here
                    }
                }
            }
            context("my seconds context for testing") {
                //second context specific code
            }
        }
    })
    

    Hope it will be useful for someone, who had the same problem