Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Cooking with Spek

AppFoundry
September 22, 2016

Cooking with Spek

Presentation by Simon Vergauwen about Spek.
Spek is a BDD testing framework in Kotlin, written by Jetbrains. This talk also covers advanced testing with unMock, Mockito & PowerMock.

AppFoundry

September 22, 2016
Tweet

More Decks by AppFoundry

Other Decks in Technology

Transcript

  1. Cooking with Spek

    View Slide

  2. Your host
    Simon Vergauwen
    Android developer
    [email protected]
    @vergauwen_simon

    View Slide

  3. AppFoundry

    View Slide

  4. A Specification Framework

    View Slide

  5. Cooking with Spek
    Spek
    Clean Testing
    Basic spek test
    Spek structure
    Iterative testing
    Technology Compatibility Kits (TCK)

    View Slide

  6. A Specification Framework

    View Slide

  7. Basic Spek test

    View Slide

  8. Coding Example
    Simple Method
    @file:JvmName("JsonUtil")

    package be.appfoundry.spekdemo.util


    import android.support.v4.util.ArrayMap

    import android.util.JsonWriter

    import java.io.StringWriter
    fun writeToJson(values: ArrayMap): String {

    // Impl

    }

    View Slide

  9. Coding Example
    JUnit Test
    public class JsonUtilUnitTest {


    ArrayMap arrayMap;


    @Before

    public void setUp() throws Exception {

    arrayMap = new ArrayMap<>();

    }

    @After

    public void tearDown() throws Exception { }

    }

    View Slide

  10. Coding Example
    JUnit Test
    public class JsonUtilUnitTest {


    ArrayMap arrayMap;


    @Before

    public void setUp() throws Exception {

    arrayMap = new ArrayMap<>();

    }

    @Test

    public void testWriteSingleMapToJSON() {

    arrayMap.put("A","B");

    String json = JsonUtil.writeToJson(arrayMap);

    assertThat(json, containsString("\"A\":\"B\""));

    }


    @After

    public void tearDown() throws Exception { }

    }

    View Slide

  11. Coding Example
    JUnit Test

    View Slide

  12. Coding Example
    Spek setup
    //setup kotlin
    testCompile "org.jetbrains.kotlin:kotlin-stdlib:1.0.3"

    testCompile “org.jetbrains.kotlin:kotlin-test:1.0.3”
    //setup spek

    testCompile "org.jetbrains.spek:spek-api:1.0.89"

    testCompile “org.jetbrains.spek:spek-junit-platform-engine:1.0.89"
    //setup unit platform runner

    testCompile "org.junit.platform:junit-platform-runner:1.0.0-M2"

    View Slide

  13. Coding Example
    Basic Spek test
    @RunWith(JUnitPlatform::class)
    class JsonWriterUnitTest : Spek({

    })

    View Slide

  14. Coding Example
    Basic Spek test
    @RunWith(JUnitPlatform::class)
    class JsonWriterUnitTest : Spek({

    context("A json writer") {


    }

    })

    View Slide

  15. Coding Example
    Basic Spek test
    @RunWith(JUnitPlatform::class)
    class JsonWriterUnitTest : Spek({

    context("A json writer") {

    given("a single value map") {

    val singleValueMap = arrayMapOf(Pair("A", "B"))

    }

    }

    })

    View Slide

  16. Coding Example
    Basic Spek test
    @RunWith(JUnitPlatform::class)

    class JsonWriterUnitTest : Spek({

    context("A json writer") {

    given("a single value map") {

    val singleValueMap = arrayMapOf(Pair("A", "B"))


    it("should contain A:B") {

    val json = writeToJson(singleValueMap)

    assertThat(json, containsString("\"A\":\"B\""))

    }

    }

    }

    })

    View Slide

  17. Coding Example
    Basic Spek test

    View Slide

  18. Coding Example
    Simple Method
    @file:JvmName("JsonUtil")

    package be.appfoundry.spekdemo.util


    import android.support.v4.util.ArrayMap

    import android.util.JsonWriter

    import java.io.StringWriter
    fun writeToJson(values: ArrayMap): String {

    // Impl

    }

    View Slide

  19. Coding Example
    Simple Method
    java.lang.RuntimeException: Method beginObject in android.util.JsonWriter not
    mocked. See http://g.co/androidstudio/not-mocked for details.
    at android.util.JsonWriter.beginObject(JsonWriter.java)
    at be.appfoundry.spekdemo.util.JsonUtil.writeToJson(JsonWriter.kt:12)
    at be.appfoundry.spekdemo.util.JsonWriterSpekTest
    $1$1$1$1.invoke(JsonWriterSpekTest.kt:21)
    at be.appfoundry.spekdemo.util.JsonWriterSpekTest
    $1$1$1$1.invoke(JsonWriterSpekTest.kt:14)
    at org.jetbrains.spek.engine.Scope$Test.execute(Scope.kt:110)
    at org.jetbrains.spek.engine.Scope$Test.execute(Scope.kt:83)

    View Slide

  20. Coding Example
    unMock
    apply plugin: 'de.mobilej.unmock'

    unMock {

    keep "android.webkit.URLUtil"

    keepStartingWith "android.util."

    }
    classpath “de.mobilej.unmock:UnMockPlugin:0.5.0”
    https://github.com/bjoernQ/unmock-plugin

    View Slide

  21. Spek structure

    View Slide

  22. Coding Example
    Spek structure
    @RunWith(JUnitPlatform::class)

    class JsonWriterUnitTest : Spek({

    context("A json writer") {

    given("a single value map") {

    val singleValueMap = arrayMapOf(Pair("A", "B"))


    it("should contain A:B") {

    val json = writeToJson(singleValueMap)

    assertThat(json, containsString("\"A\":\"B\""))

    }

    }

    }

    })

    View Slide

  23. Coding Example
    Spek structure
    @RunWith(JUnitPlatform::class)

    class JsonWriterUnitTest : Spek({

    context("A json writer") {

    given("a single value map") {

    val singleValueMap = arrayMapOf(Pair("A", "B"))


    it("should contain A:B") {

    val json = writeToJson(singleValueMap)

    assertThat(json, containsString("\"A\":\"B\""))

    }

    }

    }

    })

    View Slide

  24. Coding Example
    Spek structure
    @RunWith(JUnitPlatform::class)

    class JsonWriterUnitTest : Spek({

    context("A json writer") {

    given("a single value map") {

    val singleValueMap = arrayMapOf(Pair("A", "B"))


    it("should contain A:B") {

    val json = writeToJson(singleValueMap)

    assertThat(json, containsString("\"A\":\"B\""))

    }

    }

    }

    })
    abstract class Spek(val spec: Dsl.() -> Unit)

    View Slide

  25. Coding Example
    Spek structure
    @RunWith(JUnitPlatform::class)

    class JsonWriterUnitTest : Spek({

    context("A json writer") {

    given("a single value map") {

    val singleValueMap = arrayMapOf(Pair("A", "B"))


    it("should contain A:B") {

    val json = writeToJson(singleValueMap)

    assertThat(json, containsString("\"A\":\"B\""))

    }

    }

    }

    })

    View Slide

  26. Coding Example
    Spek structure
    @RunWith(JUnitPlatform::class)

    class JsonWriterUnitTest : Spek({

    context("A json writer") {
    beforeEach {//context beforeEach }

    given("a single value map") {

    val singleValueMap = arrayMapOf(Pair("A", "B"))


    it("should contain A:B") {

    val json = writeToJson(singleValueMap)

    assertThat(json, containsString("\"A\":\"B\""))

    }

    }
    afterEach {//context afterEach }

    }

    })

    View Slide

  27. Coding Example
    Spek structure
    @RunWith(JUnitPlatform::class)

    class JsonWriterUnitTest : Spek({

    context("A json writer") {

    given("a single value map") {

    val singleValueMap = arrayMapOf(Pair("A", "B"))


    it("should contain A:B") {

    val json = writeToJson(singleValueMap)

    assertThat(json, containsString("\"A\":\"B\""))

    }

    }

    }

    })

    View Slide

  28. Coding Example
    Spek structure
    @RunWith(JUnitPlatform::class)

    class JsonWriterUnitTest : Spek({

    context("A json writer") {

    given("a single value map") {

    val singleValueMap = arrayMapOf(Pair("A", "B"))

    beforeEach { //given beforeEach }

    it("should contain A:B") {

    val json = writeToJson(singleValueMap)

    assertThat(json, containsString("\"A\":\"B\""))

    }
    afterEach { //given beforeEach }

    }

    }

    })

    View Slide

  29. Coding Example
    Spek structure
    @RunWith(JUnitPlatform::class)

    class JsonWriterUnitTest : Spek({

    context("A json writer") {
    beforeEach {//context beforeEach }

    given("a single value map") {

    val singleValueMap = arrayMapOf(Pair("A", "B"))

    beforeEach { //given beforeEach }

    it("should contain A:B") {

    val json = writeToJson(singleValueMap)

    assertThat(json, containsString("\"A\":\"B\""))

    }
    afterEach { //given beforeEach }

    }
    afterEach {//context afterEach }

    }

    })

    View Slide

  30. Coding Example
    Spek structure
    @RunWith(JUnitPlatform::class)

    class JsonWriterUnitTest : Spek({

    context("A json writer") {

    given("a single value map") {

    val singleValueMap = arrayMapOf(Pair("A", "B"))

    then("should contain A:B") {

    val json = writeToJson(singleValueMap)

    assertThat(json, containsString("\"A\":\"B\""))

    }

    }

    }

    })
    fun Dsl.then(description: String, body: () -> Unit) {

    test("then $description", body = body)

    }

    View Slide

  31. Coding Example
    Spek structure
    @RunWith(JUnitPlatform::class)

    class JsonWriterUnitTest : Spek({

    context("A json writer") {

    given("a single value map") {

    val singleValueMap = arrayMapOf(Pair("A", "B"))

    xit("should contain A:B",reason = "pending version 2") {

    val json = writeToJson(singleValueMap)

    assertThat(json, containsString("\"A\":\"B\""))

    }
    }

    }

    })

    View Slide

  32. Iterative testing

    View Slide

  33. Coding Example
    JUnit example
    @RunWith(Parameterized.class)

    public class MainPresenterUnitTest {


    @Parameters

    public static String[] data() {

    return new String[]{"[email protected]", "[email protected]",

    [email protected]", "@AppFoundryBE"};

    }


    public MainPresenterUnitTest(String input) {

    this.input = input;

    }

    }

    View Slide

  34. Coding Example
    JUnit example
    @RunWith(Parameterized.class)

    public class MainPresenterUnitTest {


    @Parameters

    public static String[] data() {

    return new String[]{"[email protected]", "[email protected]",

    [email protected]", "@AppFoundryBE"};

    }


    public MainPresenterUnitTest(String input) {

    this.input = input;

    }


    @Before

    public void setUp() {

    mainView = mock(MainView.class);

    mainPresenter = new MainPresenter();

    mainPresenter.attachView(mainView);

    }


    @Test

    public void testIfItemIsShowAsEmail() {

    mainPresenter.processText(input);

    verify(mainView, times(1)).showItem(argThat(isA(MailItem.class)));

    }

    }

    View Slide

  35. Coding Example
    JUnit example

    View Slide

  36. Coding Example
    Iterative testing
    @RunWith(JUnitPlatform::class)

    class MainPresenterSpekTest : Spek({

    describe("The MainPresenter is handling the MainView") {

    var mainPresenter = MainPresenter()

    var mainView = mock()


    beforeEach {

    mainPresenter = MainPresenter()

    mainView = mock()

    mainPresenter.attachView(mainView)

    }


    on("processing a correctly formatted email adres") {

    listOf("[email protected]", "[email protected]",

    "[email protected]", "@AppFoundryBE").forEach { email ->

    it("should show $email") {

    mainPresenter.processText(email)

    verify(mainView, times(1)).showItem(argThat(isA(MailItem::class.java)))

    }

    }

    }

    }

    })

    View Slide

  37. Coding Example
    Iterative testing
    @RunWith(JUnitPlatform::class)

    class MainPresenterSpekTest : Spek({

    describe("The MainPresenter is handling the MainView") {

    var mainPresenter = MainPresenter()

    var mainView = mock()


    beforeEach {

    mainPresenter = MainPresenter()

    mainView = mock()

    mainPresenter.attachView(mainView)

    }


    on("processing a correctly formatted email adres") {

    listOf("[email protected]", "[email protected]",

    "[email protected]", "@AppFoundryBE").forEach { email ->

    it("should show $email") {

    mainPresenter.processText(email)

    verify(mainView, times(1)).showItem(argThat(isA(MailItem::class.java)))

    }

    }

    }

    }

    })

    View Slide

  38. Coding Example
    Iterative testing
    @RunWith(JUnitPlatform::class)

    class MainPresenterSpekTest : Spek({

    describe("The MainPresenter is handling the MainView") {

    var mainPresenter = MainPresenter()

    var mainView = mock()


    beforeEach {

    mainPresenter = MainPresenter()

    mainView = mock()

    mainPresenter.attachView(mainView)

    }


    on("processing a correctly formatted email adres") {

    listOf("[email protected]", "[email protected]",

    "[email protected]", "@AppFoundryBE").forEach { email ->

    it("should show $email") {

    mainPresenter.processText(email)

    verify(mainView, times(1)).showItem(argThat(isA(MailItem::class.java)))

    }

    }

    }

    }

    })

    View Slide

  39. Coding Example
    Iterative testing
    @RunWith(JUnitPlatform::class)

    class MainPresenterSpekTest : Spek({

    describe("The MainPresenter is handling the MainView") {

    var mainPresenter = MainPresenter()

    var mainView = mock()


    beforeEach {

    mainPresenter = MainPresenter()

    mainView = mock()

    mainPresenter.attachView(mainView)

    }


    on("processing a correctly formatted email adres") {

    listOf("[email protected]", "[email protected]",

    "[email protected]", "@AppFoundryBE").forEach { email ->

    it("should show $email") {

    mainPresenter.processText(email)

    verify(mainView, times(1)).showItem(argThat(isA(MailItem::class.java)))

    }

    }

    }

    }

    })

    View Slide

  40. Coding Example
    Iterative testing

    View Slide

  41. Technology Compatibility Kits (TCK)

    View Slide

  42. TCK
    Item
    MailItem PhoneItem TwitterItem URLItem

    View Slide

  43. Coding Example
    TCK
    abstract class ItemTCK(val item: Item) : Spek({

    //spek test

    })


    class URLItemTCKTest : ItemTCK(URLItem("www.appfoundry.be"))


    class TwitterItemTCKTest : ItemTCK(TwitterItem("@AppFoundryBE"))


    class MailItemTCKTest : ItemTCK(MailItem("[email protected]"))


    class PhoneItemTCKTest : ItemTCK(PhoneItem("003238719966"))

    View Slide

  44. Coding Example
    TCK
    abstract class ItemTCK(val factory: () -> Item) : Spek({

    val item = factory()
    //spek test

    })
    class URLItemTCKTest : ItemTCK({ URLItem("www.appfoundry.be") })


    class TwitterItemTCKTest : ItemTCK({ TwitterItem("@AppFoundryBE") })


    class MailItemTCKTest : ItemTCK({ MailItem("[email protected]") })


    class PhoneItemTCKTest : ItemTCK({ PhoneItem("003238719966") })

    View Slide

  45. Coding Example
    TCK
    @RunWith(PowerMockRunner::class)

    @PowerMockRunnerDelegate(JUnitPlatform::class)

    @PrepareForTest(ContextCompat::class)

    abstract class ItemTCK(val factory: () -> Item) : Spek({


    })

    View Slide

  46. Coding Example
    TCK
    @RunWith(PowerMockRunner::class)

    @PowerMockRunnerDelegate(JUnitPlatform::class)

    @PrepareForTest(ContextCompat::class)

    abstract class ItemTCK(val factory: () -> Item) : Spek({

    val item = factory()

    describe("handling an item object") {

    val context = mock()

    val drawable = mock()

    beforeEach {

    PowerMock.mockStatic(ContextCompat::class.java)

    whenever(ContextCompat.getDrawable(any(), any()))

    .thenReturn(drawable)

    }


    }

    })

    View Slide

  47. Coding Example
    TCK
    @RunWith(PowerMockRunner::class)

    @PowerMockRunnerDelegate(JUnitPlatform::class)

    @PrepareForTest(ContextCompat::class)

    abstract class ItemTCK(val factory: () -> Item) : Spek({

    val item = factory()

    describe("handling an item object") {

    val context = mock()

    val drawable = mock()

    beforeEach {

    PowerMock.mockStatic(ContextCompat::class.java)

    whenever(ContextCompat.getDrawable(any(), any()))

    .thenReturn(drawable)

    }


    it("should never have a null name") {

    assertThat(item.name, notNullValue())

    }

    it("should always have a color id") {

    assertThat(item.itemColorId, isA(Int::class.java))

    }

    it("should always return a drawable") {

    assertThat(item.getIcon(context), equalTo(drawable))

    }

    }

    })

    View Slide

  48. Coding Example
    TCK

    View Slide

  49. Simplify your tests

    View Slide

  50. Coding Example
    Simple Rx function
    fun doSomeRxing(): Observable =

    Observable.just(1L).observeOn(AndroidSchedulers.mainThread())

    View Slide

  51. Coding Example
    Simple Rx function
    java.lang.ExceptionInInitializerError
    at rx.android.schedulers.AndroidSchedulers.mainThread(AndroidSchedulers.java:39)
    at be.appfoundry.spekdemo.util.RxMethodKt.doSomeRxing(RxMethod.kt:8)
    at be.appfoundry.spekdemo.util.RxTest$1$1$2.invoke(RxTest.kt:22)
    at be.appfoundry.spekdemo.util.RxTest$1$1$2.invoke(RxTest.kt:16)
    at org.jetbrains.spek.engine.Scope$Test.execute(Scope.kt:110)
    at org.jetbrains.spek.engine.Scope$Test.execute(Scope.kt:83)
    ...


    Caused by: java.lang.RuntimeException: Method getMainLooper in android.os.Looper not
    mocked. See http://g.co/androidstudio/not-mocked for details.
    at android.os.Looper.getMainLooper(Looper.java)
    at rx.android.schedulers.AndroidSchedulers
    $MainThreadSchedulerHolder.(AndroidSchedulers.java:31)
    ... 32 more

    View Slide

  52. Coding Example
    JUnit rule for Rx
    public class RxJavaResetRule implements TestRule {

    @Override

    public Statement apply(final Statement base, final Description description) {


    }
    }

    View Slide

  53. Coding Example
    JUnit rule for Rx
    public class RxJavaResetRule implements TestRule {

    @Override

    public Statement apply(final Statement base, final Description description) {

    return new Statement() {

    @Override

    public void evaluate() throws Throwable {

    //@Before


    base.evaluate();


    //@After

    } 

    };

    }
    }

    View Slide

  54. Coding Example
    JUnit rule for Rx
    public class RxJavaResetRule implements TestRule {

    @Override

    public Statement apply(final Statement base, final Description description) {

    return new Statement() {

    @Override

    public void evaluate() throws Throwable {

    //@Before

    RxJavaPlugins.getInstance().reset();

    RxJavaPlugins.getInstance().registerSchedulersHook(rxJavaHook);


    RxAndroidPlugins.getInstance().reset();

    RxAndroidPlugins.getInstance().registerSchedulersHook(rxAndroidHook);


    base.evaluate();


    //@After

    RxJavaPlugins.getInstance().reset();

    RxAndroidPlugins.getInstance().reset();

    } 

    };

    }
    //rxJavaHook & rxAndroidHook def
    }

    View Slide

  55. Coding Example
    JUnit rule for Rx
    public class RxUnitTest {

    @Rule

    public RxJavaResetRule rxJavaResetRule = new RxJavaResetRule();


    TestSubscriber testSubscriber;


    @Before

    public void setUp() {

    testSubscriber = new TestSubscriber<>();

    }

    }

    View Slide

  56. Coding Example
    JUnit rule for Rx
    public class RxUnitTest {

    @Rule

    public RxJavaResetRule rxJavaResetRule = new RxJavaResetRule();


    TestSubscriber testSubscriber;


    @Before

    public void setUp() {

    testSubscriber = new TestSubscriber<>();

    }


    @Test

    public void testRx() {

    RxMethodKt.doSomeRxing().subscribe(testSubscriber);


    assertThat(testSubscriber,

    allOf(onNextEvents(hasSize(1)),
    onNextEvents(hasItem(1L)),

    hasNoErrors(),

    isCompleted()));

    }

    }

    View Slide

  57. Coding Example
    Junit rule in Spek
    @RunWith(JUnitPlatform::class)

    class RxTest : Spek({

    on("testing something observed by an android schedulers") {

    var testSubscriber = TestSubscriber()

    beforeEach { testSubscriber = TestSubscriber() }

    }

    })

    View Slide

  58. Coding Example
    Junit rule in Spek
    @RunWith(JUnitPlatform::class)

    class RxTest : Spek({

    on("testing something observed by an android schedulers") {

    var testSubscriber = TestSubscriber()

    beforeEach { testSubscriber = TestSubscriber() }


    it("should be easily tested in unit test") {

    doSomeRxing().subscribe(testSubscriber)

    assertThat(testSubscriber,

    allOf(hasNoErrors(),

    onNextEvents(hasSize(1)),

    onNextEvents(hasItem(1L)),

    isCompleted()))

    }

    }

    })

    View Slide

  59. Coding Example
    Simple Rx function
    inline fun Dsl.rxGroup(description: String, pending: Pending = Pending.No,

    crossinline body: Dsl.() -> Unit) {


    group(description, pending) {

    }

    }

    View Slide

  60. Coding Example
    Simple Rx function
    inline fun Dsl.rxGroup(description: String, pending: Pending = Pending.No,

    crossinline body: Dsl.() -> Unit) {


    group(description, pending) {

    beforeEach {

    RxJavaPlugins.getInstance().reset()

    RxJavaPlugins.getInstance().registerSchedulersHook(rxJavaHook)


    RxAndroidPlugins.getInstance().reset()

    RxAndroidPlugins.getInstance().registerSchedulersHook(rxAndroidHook)

    }


    body()


    afterEach {

    RxJavaPlugins.getInstance().reset()


    RxAndroidPlugins.getInstance().reset()

    }

    }

    }

    View Slide

  61. Coding Example
    Simple Rx function
    inline fun Dsl.rxGroup(description: String, pending: Pending = Pending.No,

    crossinline body: Dsl.() -> Unit) {


    group(description, pending) {

    beforeEach {

    RxJavaPlugins.getInstance().reset()

    RxJavaPlugins.getInstance().registerSchedulersHook(rxJavaHook)


    RxAndroidPlugins.getInstance().reset()

    RxAndroidPlugins.getInstance().registerSchedulersHook(rxAndroidHook)

    }


    body()


    afterEach {

    RxJavaPlugins.getInstance().reset()


    RxAndroidPlugins.getInstance().reset()

    }

    }

    }
    inline fun Dsl.onRx(description: String, crossinline body: Dsl.() -> Unit) =

    rxGroup("on $description", body = body)

    View Slide

  62. Coding Example
    Simple Rx function
    @RunWith(JUnitPlatform::class)

    class RxTest : Spek({

    on("when testing something observed by an android schedulers") {

    var testSubscriber = TestSubscriber()

    beforeEach { testSubscriber = TestSubscriber() }


    it("should be easily tested in unit test") {

    doSomeRxing().subscribe(testSubscriber)

    assertThat(testSubscriber,

    allOf(hasNoErrors(),

    onNextEvents(hasSize(1)),

    onNextEvents(hasItem(1L)),

    isCompleted()))

    }

    }

    })

    View Slide

  63. Coding Example
    Simple Rx function
    @RunWith(JUnitPlatform::class)

    class RxTest : Spek({

    onRx("when testing something observed by an android schedulers") {

    var testSubscriber = TestSubscriber()

    beforeEach { testSubscriber = TestSubscriber() }


    it("should be easily tested in unit test") {

    doSomeRxing().subscribe(testSubscriber)

    assertThat(testSubscriber,

    allOf(hasNoErrors(),

    onNextEvents(hasSize(1)),

    onNextEvents(hasItem(1L)),

    isCompleted()))

    }

    }

    })

    View Slide

  64. View Slide

  65. Thank you!

    View Slide

  66. Questions?
    Simon Vergauwen
    Android developer
    [email protected]
    @vergauwen_simon

    View Slide