男もすなる Unit Test といふものを、女もしてみむとてするなり。
とりあえず読む
kotlin.test - Kotlin Programming Language
@BeforeTest
くらいかな。 mock を reset するのに使ってる。 reset のあたりのドキュメント読んだ感じだと@Test
の各論の最中に reset かますような無粋なコード書いてるとひとつのテストの中でいろんなことやりすぎじゃん?って書いてあった、ホントそうだと思う。- なんにも考えないで IDE のおっしゃるがままに補完してて気が付かなかったけど、 whenever って mockito-kotlin が定義している DSL なのね 。完全に when = 一回こっきり / whenever = 何度呼んでも、みたいなふるまいの格差があるのかなと思ってたけど名前で勘違いして思い込んでただけだった、 kotlin でテスト書くときには whenever つかおう。
- いろいろなしがらみもあって JUnit 4 なんだけど、 JUnit 5 使おうって気になるねえ。
@Test
それぞれを実行するために、デフォルトではテストクラスのインスタンスをそれぞれ作ってる、やっぱりそうなんだ。そのかわり並列に実行できるってことかな。再利用するようにして@BeforeTest
で reset するようになったらインスタンス生成は押さえられるけど直列に実行することにはなるよねえ?2 s vs 250 ms in the example
とは書いてあったので再利用によって 1/8 くらいに時間節約される方が効くよってことかな。- data class の
toString()
はたしかにステキ。
data class Item(val itemId: Long, val itemName: String)
interface FooBarContract { interface Presenter { fun showItem(itemId: Long) } interface ViewProxy { fun bind(presenter: Presenter) fun showItemName(itemName: String) fun showItemId(itemId: Long) fun showError(message: String) } interface Repository { fun fetchItem(itemId: Long): Single<Item> } }
class ItemNotFoundException(val message: String): Exception(message)
class FooBarPresenter( private val repository: FooBarContract.Repository, private val viewProxy: FooBarContract.Presenter, private val uiScheduler: Scheduler = AndroidSchedulers.mainThread(), private val disposables: CompositeDisposable = CompositeDisposable() ) : FooBarContract.Presenter { init { viewProxy.bind(presenter) } override fun showItem(itemId: Long) { repository.fetchItem(itemId) .observeOn(uiScheduler) .subscribe( this::showItem, { e -> viewProxy.showError(e.message)} ).let(disposables::add) } @VisibleForTesting internal fun showItem(item: Item) { viewProxy.showItemName(item.itemName) viewProxy.showItemId(item.itemId) } }
class FooBarPresenterTest { private val item1L: Item = Item(1L, "existing_item") private val repository: FooBarContract.Repository = mock { on { fetchItem(1L) }.thenReturn { Single.just(Item1L) } } private val viewProxy: FooBarContract.ViewProxy = mock() private val uiScheduler: Scheduler = Schedulers.trampoline() private val disposables: CompositeDisposable = mock() private val presenter = FooBarPresenter(repository, viewProxy, uiScheduler, disposables) @Test fun `viewProxy should be bind to presenter when initializing the presenter`() { verify(viewProxy).bind(presenter) } @Test fun `showItem(itemId) should show an Item with the ID if exists`() { val presenter = this.presenter.let(::spy) presenter.fetchItem(1L) verify(presenter).showItem(item1L) } @Test fun `showItem(itemId) should show error if not exist`() { val presenter = this.presenter.let(::spy) val itemId = 2L val errorMessage = "item ID $itemId is not found" doReturn(Single.error<Item>(ItemNotFoundException(errorMessage)) .whenever(repository).fetchItem(itemId) presenter.fetchItem(itemId) verify(presenter, never()).showItem(any()) verify(viewProxy).showError(errorMessage) } @Test fun `showItem(item) should show the item`() { presenter.showItem(item1L) verify(viewProxy).run { showItemName(item1L.itemName) showItemId(item1L.itemId) } } }
この場でテキトーに書いてるから、 import とか書いてないケド許して。
ひとこと
えーっと、なんだろう、ホットカーペット買おうかなっ
今日から俺は!!
つらい日もつらいときも、きっとたぶん、毎日ブログを書いていくから。