Cooking with Spek

103e1ebcacd620770cf32a36b9aba17e?s=47 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.

103e1ebcacd620770cf32a36b9aba17e?s=128

AppFoundry

September 22, 2016
Tweet

Transcript

  1. Cooking with Spek

  2. Your host Simon Vergauwen Android developer simon.vergauwen@appfoundry.be @vergauwen_simon

  3. AppFoundry

  4. A Specification Framework

  5. Cooking with Spek Spek Clean Testing Basic spek test Spek

    structure Iterative testing Technology Compatibility Kits (TCK)
  6. A Specification Framework

  7. Basic Spek test

  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, String>): String {
 // Impl
 }
  9. Coding Example JUnit Test public class JsonUtilUnitTest {
 
 ArrayMap<String,String>

    arrayMap;
 
 @Before
 public void setUp() throws Exception {
 arrayMap = new ArrayMap<>();
 } 
 @After
 public void tearDown() throws Exception { }
 }
  10. Coding Example JUnit Test public class JsonUtilUnitTest {
 
 ArrayMap<String,String>

    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 { }
 }
  11. Coding Example JUnit Test

  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"
  13. Coding Example Basic Spek test @RunWith(JUnitPlatform::class) class JsonWriterUnitTest : Spek({


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


    context("A json writer") {
 
 }
 })
  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"))
 }
 }
 })
  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\""))
 }
 }
 }
 })
  17. Coding Example Basic Spek test

  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, String>): String {
 // Impl
 }
  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)
  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
  21. Spek structure

  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\""))
 }
 }
 }
 })
  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\""))
 }
 }
 }
 })
  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)
  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\""))
 }
 }
 }
 })
  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 }
 }
 })
  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\""))
 }
 }
 }
 })
  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 }
 }
 }
 })
  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 }
 }
 })
  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)
 }
  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\""))
 } }
 }
 })
  32. Iterative testing

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


    @Parameters
 public static String[] data() {
 return new String[]{"test@appfoundry.be", "ttest@fffff.org",
 “bbbb@bbbbb.com", "@AppFoundryBE"};
 }
 
 public MainPresenterUnitTest(String input) {
 this.input = input;
 }
 }
  34. Coding Example JUnit example @RunWith(Parameterized.class)
 public class MainPresenterUnitTest {
 


    @Parameters
 public static String[] data() {
 return new String[]{"test@appfoundry.be", "ttest@fffff.org",
 “bbbb@bbbbb.com", "@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)));
 }
 }
  35. Coding Example JUnit example

  36. Coding Example Iterative testing @RunWith(JUnitPlatform::class)
 class MainPresenterSpekTest : Spek({
 describe("The

    MainPresenter is handling the MainView") {
 var mainPresenter = MainPresenter()
 var mainView = mock<View>()
 
 beforeEach {
 mainPresenter = MainPresenter()
 mainView = mock<View>()
 mainPresenter.attachView(mainView)
 }
 
 on("processing a correctly formatted email adres") {
 listOf("test@appfoundry.be", "ttest@fffff.org",
 "bbbb@bbbbb.com", "@AppFoundryBE").forEach { email ->
 it("should show $email") {
 mainPresenter.processText(email)
 verify(mainView, times(1)).showItem(argThat(isA(MailItem::class.java)))
 }
 }
 }
 }
 })
  37. Coding Example Iterative testing @RunWith(JUnitPlatform::class)
 class MainPresenterSpekTest : Spek({
 describe("The

    MainPresenter is handling the MainView") {
 var mainPresenter = MainPresenter()
 var mainView = mock<MainView>()
 
 beforeEach {
 mainPresenter = MainPresenter()
 mainView = mock<MainView>()
 mainPresenter.attachView(mainView)
 }
 
 on("processing a correctly formatted email adres") {
 listOf("test@appfoundry.be", "ttest@fffff.org",
 "bbbb@bbbbb.com", "@AppFoundryBE").forEach { email -> 
 it("should show $email") {
 mainPresenter.processText(email)
 verify(mainView, times(1)).showItem(argThat(isA(MailItem::class.java)))
 }
 }
 }
 }
 })
  38. Coding Example Iterative testing @RunWith(JUnitPlatform::class)
 class MainPresenterSpekTest : Spek({
 describe("The

    MainPresenter is handling the MainView") {
 var mainPresenter = MainPresenter()
 var mainView = mock<View>()
 
 beforeEach {
 mainPresenter = MainPresenter()
 mainView = mock<View>()
 mainPresenter.attachView(mainView)
 }
 
 on("processing a correctly formatted email adres") {
 listOf("test@appfoundry.be", "ttest@fffff.org",
 "bbbb@bbbbb.com", "@AppFoundryBE").forEach { email -> 
 it("should show $email") {
 mainPresenter.processText(email)
 verify(mainView, times(1)).showItem(argThat(isA(MailItem::class.java)))
 }
 }
 }
 }
 })
  39. Coding Example Iterative testing @RunWith(JUnitPlatform::class)
 class MainPresenterSpekTest : Spek({
 describe("The

    MainPresenter is handling the MainView") {
 var mainPresenter = MainPresenter()
 var mainView = mock<View>()
 
 beforeEach {
 mainPresenter = MainPresenter()
 mainView = mock<View>()
 mainPresenter.attachView(mainView)
 }
 
 on("processing a correctly formatted email adres") {
 listOf("test@appfoundry.be", "ttest@fffff.org",
 "bbbb@bbbbb.com", "@AppFoundryBE").forEach { email -> 
 it("should show $email") {
 mainPresenter.processText(email)
 verify(mainView, times(1)).showItem(argThat(isA(MailItem::class.java)))
 }
 }
 }
 }
 })
  40. Coding Example Iterative testing

  41. Technology Compatibility Kits (TCK)

  42. TCK Item MailItem PhoneItem TwitterItem URLItem

  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("simon.vergauwen@appfoundry.be"))
 
 class PhoneItemTCKTest : ItemTCK(PhoneItem("003238719966"))
  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("simon.vergauwen@appfoundry.be") })
 
 class PhoneItemTCKTest : ItemTCK({ PhoneItem("003238719966") })
  45. Coding Example TCK @RunWith(PowerMockRunner::class)
 @PowerMockRunnerDelegate(JUnitPlatform::class)
 @PrepareForTest(ContextCompat::class)
 abstract class ItemTCK(val factory:

    () -> Item) : Spek({
 
 })
  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<Context>()
 val drawable = mock<Drawable>()
 beforeEach {
 PowerMock.mockStatic(ContextCompat::class.java)
 whenever(ContextCompat.getDrawable(any<Context>(), any<Int>()))
 .thenReturn(drawable)
 }
 
 }
 })
  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<Context>()
 val drawable = mock<Drawable>()
 beforeEach {
 PowerMock.mockStatic(ContextCompat::class.java)
 whenever(ContextCompat.getDrawable(any<Context>(), any<Int>()))
 .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))
 }
 }
 })
  48. Coding Example TCK

  49. Simplify your tests

  50. Coding Example Simple Rx function fun doSomeRxing(): Observable<Long> =
 Observable.just(1L).observeOn(AndroidSchedulers.mainThread())

  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.<clinit>(AndroidSchedulers.java:31) ... 32 more
  52. Coding Example JUnit rule for Rx public class RxJavaResetRule implements

    TestRule {
 @Override
 public Statement apply(final Statement base, final Description description) {
 
 } }
  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
 } 
 };
 } }
  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 }
  55. Coding Example JUnit rule for Rx public class RxUnitTest {


    @Rule
 public RxJavaResetRule rxJavaResetRule = new RxJavaResetRule();
 
 TestSubscriber<Long> testSubscriber;
 
 @Before
 public void setUp() {
 testSubscriber = new TestSubscriber<>();
 }
 }
  56. Coding Example JUnit rule for Rx public class RxUnitTest {


    @Rule
 public RxJavaResetRule rxJavaResetRule = new RxJavaResetRule();
 
 TestSubscriber<Long> 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()));
 }
 }
  57. Coding Example Junit rule in Spek @RunWith(JUnitPlatform::class)
 class RxTest :

    Spek({
 on("testing something observed by an android schedulers") {
 var testSubscriber = TestSubscriber<Long>()
 beforeEach { testSubscriber = TestSubscriber<Long>() } 
 }
 })
  58. Coding Example Junit rule in Spek @RunWith(JUnitPlatform::class)
 class RxTest :

    Spek({
 on("testing something observed by an android schedulers") {
 var testSubscriber = TestSubscriber<Long>()
 beforeEach { testSubscriber = TestSubscriber<Long>() }
 
 it("should be easily tested in unit test") {
 doSomeRxing().subscribe(testSubscriber) 
 assertThat(testSubscriber,
 allOf(hasNoErrors(),
 onNextEvents(hasSize<Any>(1)),
 onNextEvents(hasItem(1L)),
 isCompleted()))
 }
 }
 })
  59. Coding Example Simple Rx function inline fun Dsl.rxGroup(description: String, pending:

    Pending = Pending.No,
 crossinline body: Dsl.() -> Unit) {
 
 group(description, pending) {
 }
 }
  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()
 }
 }
 }
  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)
  62. Coding Example Simple Rx function @RunWith(JUnitPlatform::class)
 class RxTest : Spek({


    on("when testing something observed by an android schedulers") {
 var testSubscriber = TestSubscriber<Long>()
 beforeEach { testSubscriber = TestSubscriber<Long>() }
 
 it("should be easily tested in unit test") {
 doSomeRxing().subscribe(testSubscriber) 
 assertThat(testSubscriber,
 allOf(hasNoErrors(),
 onNextEvents(hasSize<Any>(1)),
 onNextEvents(hasItem(1L)),
 isCompleted()))
 }
 }
 })
  63. Coding Example Simple Rx function @RunWith(JUnitPlatform::class)
 class RxTest : Spek({


    onRx("when testing something observed by an android schedulers") {
 var testSubscriber = TestSubscriber<Long>()
 beforeEach { testSubscriber = TestSubscriber<Long>() }
 
 it("should be easily tested in unit test") {
 doSomeRxing().subscribe(testSubscriber) 
 assertThat(testSubscriber,
 allOf(hasNoErrors(),
 onNextEvents(hasSize<Any>(1)),
 onNextEvents(hasItem(1L)),
 isCompleted()))
 }
 }
 })
  64. None
  65. Thank you!

  66. Questions? Simon Vergauwen Android developer simon.vergauwen@appfoundry.be @vergauwen_simon