いわたんち

いわたんちは概念となりました

SpekでgetMainLooperをMockする必要がある場合の対応方法

LiveData使ってるクラスのテストをSpekで書こうとするとよく出てくるエラー。

エラー内容

Method getMainLooper in android.os.Looper not mocked. See http://g.co/androidstudio/not-mocked for details.

テスト用のTaskExecutorを用意する

class TestArchTaskExecutor : TaskExecutor() {
    override fun executeOnDiskIO(runnable: Runnable) {
        runnable.run()
    }

    override fun isMainThread(): Boolean {
        return true
    }

    override fun postToMainThread(runnable: Runnable) {
        runnable.run()
    }
}

SpekのGroupBodyに拡張関数を追加

fun GroupBody.applyTestTaskExecutor(executor: TaskExecutor = TestArchTaskExecutor()) {
    beforeGroup {
        // AACのテスト用設定
        ArchTaskExecutor.getInstance().setDelegate(executor)
    }
    afterGroup {
        ArchTaskExecutor.getInstance().setDelegate(null)
    }
}

テストでSpekのBodyで拡張関数を呼び出す

internal object ViewModelTest : Spek({
    applyTestTaskExecutor()
    
    Feature("...") {}
}

pandasでステップ状のグラフを書く

import pandas as pd
df = pd.DataFrame([1, 3 , 5])
df.plot(marker='.')
df.plot(marker='x', drawstyle='steps-post') # drawstyleで指定

drawstyleで steps-post を指定するとステップ状になる

指定なしの場合

f:id:iwata_n:20200807150213p:plain

指定ありの場合

f:id:iwata_n:20200807150228p:plain

格安HDMIキャプチャでRX100M3をWebカメラ化する

f:id:iwata_n:20200710102713j:plain

こいつを使ってMacとRX100M3をHDMIケーブルで接続することで簡単にWEBカメラ化できた。 ケーブルは携帯用のディスプレイについてきたマイクロHDMIで接続してるけども、下に紹介してるようなケーブルでよさそう あとはモニタアームにジェネリックなゴリラポッドでくっつけて程よい高さを確保した。 ほとんど遅延もないのでZoomとかであれば高画質な顔を見せつけれそうです

mockkでメソッド呼び出しが行われていないことを確認する

mockkのMockには wasNot Called が用意されているのでメソッドにも使えるかと思ってたけども実は違っていたのでメモ

// これは間違い
verify {
  mock.method() wasNot Called
}

// コレが正しい
verify(exactly = 0) {
  mock.method()
}

じゃあ wasNot Called はいつ使うのかというと以下のようにMock全体に対して呼び出しが行われていないことを確認する時に使う。

verify {
  mock wasNot Called
}

参考: Cannot verify a mocked class's method was not called. · Issue #349 · mockk/mockk · GitHub

mockkのreturnsとanswersの違い

every {
  mock.time
} returns System.currentTimeMillis()
val a1 = mock.time
// なんか処理
val b1 = mock.time
every {
  mock.time
} answers { System.currentTimeMillis() }
val a2 = mock.time
// なんか処理
val b2 = mock.time

で挙動が違っていて、 returns だと宣言時の時刻で固定されて、 answers の場合は呼び出しのたびに評価される。 なので、a1 = b1となり、 a2 != b2 となる。