Mock the un-mockable Kotlin classes

f:id:lvla:20170430014523p:plain

tl;dr

  • Kotlinクラスはデフォルトfinalだからモックできない
  • Mockito2ならできるよ

Kotlinのクラス、関数はデフォルトでfinalだからモックできない。

Kotlinのクラス、関数はデフォルトfinalで継承もオーバーライドもできないのでMockitoでモックすることができません。
なので今までは以下のどれかでモック可能な状態にしていました。

  • openで継承可能、オーバーライド可能にする
  • All-open pluginでクラス/関数をすべてopenにする
  • Interfaceに切り出しておく
  • PoweMockを利用する

Mockito2.1.0でfinalでもモックできるようになった

Mockito2.1.0でMockMakerというものが導入されました。ただしMockMakerはまだ実験的段階で、デフォルトではオフになっています。
MockMakerを利用する方法は2つ用意されています。

MockMaker fileを用意する。

src/test/resources/mockito-extensionsディレクトリに以下の一行を含むorg.mockito.plugins.MockMakerファイルを置きます。

mock-maker-inline

たった一行のファイルを置くだけなのでそんなに手間ではないかな?

mockito-inlineを利用する

こちらはgradleでdependenciesを書き換えるだけです。

dependencies {
    //  testCompile "org.mockito:mockito-core:2.x.y"
    testCompile "org.mockito:mockito-inline:2.x.y"
}

どちらを利用するか

私は基本的にMockitoは直接扱っておらず、mockito-coreに依存しているKotlin向けのExtensionライブラリを利用しているのでMockMaker fileを置く方法を利用してます。

DroidKaigi 2017でWelcomeトークと2セッション行ってきた。

Welcomeトーク

なんかいつの間にかWelcomeトークでスピーカー代表として話してました!びっくり! 「当たり前と思ってる情報でも誰かにとっては需要がありますよ!プロポーザル出しましょう!」というお話をしました。 いつのまにかCodeZine, giyho.jpに載ってました。

セッション

speakerdeck.com

speakerdeck.com

Pairsで去年やったおっきな仕事の話と、僕がアプリつくるときによくやる構成の話です。
後者30分に対して詰め込みすぎたーと反省してます。
KotlinはJavaわかればかけるし(と、ヤギさんが言ってました!!!!)、RetrofitやOrmaはリポジトリのREADMEを読めばすすっと使えると思います。
問題はRxJavaとDagger2ですよね。僕も初めてのときは意味がわからなかったんですが何時間かコード書いて覚えました。
とりあえずRxJavaとDagger2の補足資料として以下を置いておきますが、どれかしらを読みながらコードを書いてみてその挙動を確かめるのが一番近道だと思います。

詳解RxJava:Scheduler、非同期処理、subscribe/unsubscribe - Qiita

ユースケースで入門するAndroid RxJava1 - Qiita

逆引きよく使うRxJava1.x オペレータ - Qiita

Dagger2 入門解説 - Qiita

サンプルアプリはこちら github.com

来年が楽しみだね

皆さんお疲れ様でした。DroidKaigi 2018が楽しみですね。

RxJava2へ段階的に移行しよう

RxJava2リリースされましたね、何ヶ月か前に。
OrmaやRetrofitがRxJava2に対応済みなので、個人的にもPairs的にもいつでも移行できる状態ではあります。

といっても大変ですよね。一体いくつのストリームがあると思ってるんですか!!
あれを全部RxJava2へ書き換えていくのはちょっと面倒ですね。できれば徐々に移行を進めていきたいところです。

RxJava1とRxJava2の相互運用

github.com そこで便利なのが、RxJavaやIxJavaの開発を行っているakarnokdさんが開発しているRxJava2Interopです。
こんな感じで相互に変換を行うことが可能です。

io.reactivex.Observable  o2 = RxJavaInterop.toV2Observabe(observable);
rx.Observable  o1 = RxJavaInterop.toV1Observable(observable, backpressureStrategy);

レイヤーごとにRxJava2に書き換えていく。

RxJava2Interopを利用すれば相互運用ができることがわかりました。 私の個人アプリやPairsではLayered Architectureを採用していますので、下の層の方(DaoやClient)から徐々にRxJava2に書き換えていけば良さそうです。

Kotlinでエレガントに

個人アプリもPairsもKotlinで開発しています。となると、

RxJavaInterop.toV2Observable(observable)

と書くのはちょっと面倒くさいな..となってきます。そこは拡張関数を利用して流れるように書きたいわけです。

observable.toV2Observable()

github.com というわけで、拡張関数を書いてまとめておきました。本家のメソッド名そのままに拡張関数を書いてるので、使い方に関しては本家をみてください。 面倒くさいのでまだJitPackつかってますがそのうちちゃんとあげます。 あとまだテストも書いてないです、そのうち書きます、またはPR待っています。

Java/Kotlin混合プロジェクトで気をつけていること。

弊社Slackの#eureka-kotlinからの転載シリーズ。 プライベートではすべてKotlinでコードを書いているのであまり気にすることがなかったのですが、pairsはJava/Kotlin混合で成り立っているのでKotlinで書いたコードがJavaからどう見えるのかを気にすることがたまにあります。 すべてを網羅しているわけではありませんが、いくつか書いてみました。

Companion object != Static

Kotlinにはstaticメソッドやstaticフィールドはありませんが、これらと同等の役割を持ったcompanion objectが存在します。

fun KotlinFragment: Fragment() {
    companion object {
        fun newInstance(...): KotlinFragment {
            ...
        }
    }
}

しかしこれをJavaから利用するには以下のようにする必要があります。

KotlinFragment.Companion.newInstance(...);

これはいまいちですね。Javaからの利用が想定される場合には@JvmStaticアノテーションを利用します。

fun KotlinFragment: Fragment() {
    companion object {
        @JvmStatic
        fun newInstance(...): KotlinFragment {
            ...
        }
    }
}
KotlinFragment.newInstance(...);

Property != Field

Kotlinで定義したPropertyはJavaからはアクセサを通して利用することになります。

class Kotlin {
    val bar = "bar"
}
kotlin.getBar();

これをJavaからFieldとして扱うためには@JvmFieldを利用します。

class Kotlin {
    @JvmField
    val bar = "bar"
}
kotlin.bar;

高階関数

Javaでは関数は第一級オブジェクトではありません。 関数を引数に取るKotlinの関数をJava8未満から利用する場合には、匿名クラスを渡す必要があります。

class Kotlin {
    fun higherOrder(func: ()->Unit) {
        ...
    }
}
kotlin.higerOrder(new Function0<Unit>() {
    @Override
    public Unit invoke() {
        return null;
    }
});

関数を返すKotlinの関数であれば、FunctionNが返ってきます。

class Kotlin {
    fun higherOreder(): ()->Unit {
        ...
    }
}
Function0<Unit> func = kotlin.higerOrder();

予約語

KotlinでJavaの予約後を使ってしまうと、Javaから呼び出すことができません。

kotlin.final()

JavaでKotlinの予約後を使った場合、Kotlinから呼び出すことはできますが扱いにくいです。

java.`is`()

open

これはJavaからの利用に限った話ではありませんが、Kotlinのクラス、関数はopenキーワードをつけなければすべてJavaでいうfinalです。
テストを書く際にmockitoを使ってKotlinクラスをモックしようとしても、継承/オーバーライドすることができません。
mockitoを利用する場合にはモック対象にopenキーワードをつけて継承/オーバーライド可能にしておきましょう。
Kotlin 1.0.6以降であればAll Openアノテーションを定義することで一括ですべてをopenにすることも可能です。

blog.jetbrains.com

KotlinのInterfaceはPropertyも定義できるんだよ

そういえば最近は仕事でもKotlinを書いています。同僚をKotlinで洗脳した甲斐があるというものです。 developers.eure.jp

しかし、全員が全員Javaを書く際と同じようにすらすらKotlinを書くことができるわけではないので、私がKotlinのTipsや文法などをつぶやくSlackのチャンネルがあり、みんなにはそれ読んでもらっています。 せっかくなのでこれからは自分のブログにも転載していこうと思います。 多少雑です。

tl;dr

  • KotlinのInterfaceはデフォルト実装を定義できる
  • KotlinのInterfaceは(バッキングフィールドを持たないものであれば)プロパティを定義できる

そういえば、(1.1からはできるが)data classは継承をサポートしてないので、デフォルト実装やpropertyをもつinterfaceを実装することで、なにかしら有効活用できそうですね。

KotlinのInterface

デフォルト実装を定義できる。

KotlinのInterfaceはJava8のInterfaceと同様に、デフォルト実装を定義できます。

interface Foo {
    fun doSomething() = "default"
}

class FooImpl : Foo {
    
}

// -------------------

val foo = FooImpl()
foo.doSomething() // return "default"

Propertyを定義できる。

JavaではInterfaceにフィールドを定義することはできませんでした。
しかし、KotlinではBacking fieldを持たないPropertyであれば定義する事ができます。

interface Foo {
    val one: Int
}

class FooImpl : Foo {
    override val one = 2
}

Backing Fieldとは

うまく説明できる気がしないのでKotlinコードと、そこから生成されるByteCodeをJavaデコンパイルしたものの対比をみて察してください。
Javaコード側でfieldの定義されているA, C, Eをbacking fieldを持ったpropertyといいます。

あわせて読みたい:

13日目:プロパティとフィールド - Kotlin Advent Calendar 2012 (全部俺)

case A

class Kotlin {
    val a = 2
}

public final class Java {
    private final int a = 2;

    public final int getA() {
       return this.a;
    }
}

case B

class Kotlin {
    val b: Int
        get() = 2
}

public final class Java {
    public final int getB() {
       return 2;
    }        
}

case C

class Kotlin {
    var c = 2
}

public final class Java {
   private int c = 2;

   public final int getC() {
      return this.c;
   }

   public final void setC(int <set-?>) {
      this.c = <set-?>;
   }
}

case D

class Kotlin {
    var d: Int
        get() = 2
        set(value) { }
}

public final class Java {
    public final int getD() {
       return 2;
    }
    
    public final void setD(int value) {
    }
}

case E

class Kotlin {
    var e: Int = 2
        set(value) { }
}

public final class Java {
   private int e = 2;

   public final int getE() {
      return this.e;
   }

   public final void setE(int value) {
   }
}

AnkoでLayout XMLを殺した

そういえば先月頭にRettyさんでAnkoについてLTしたのでスライド置いておきますね。
speakerdeck.com

あ、ちなみに本稿は特にAnkoの解説は行ってないポエムです。

Ankoとは

Kotlin製DSL。Viewを書くのに利用します。
XMLでView書いた場合の以下のような問題を感じてJetBrainsのエンジニアが開発したのがAnko。

  • 型安全じゃないし
  • null安全じゃないし
  • 似たようなことを毎回かかなきゃいけないし
  • XMLをパースする分パフォーマンス悪いし
  • コードの再利用できないし

こんな感じでViewを書けます

verticalLayout {
    val name = editText()
    button("Say Hello") {
        onClick { toast("Hello, ${name.text}!") }
    }
}

Javaで書いた場合との差が冒頭に貼ったスライドに書いてあるので、興味ある人は見てください。

なにこれ魔法!?🎩

Kotlinの拡張関数とプロパティを利用してます。 拡張関数を引数にとる関数がどうたらこうたら。これも冒頭のスライドに書いてあるので興味のある人は見てください。

XML全部殺してフルAnkoにした

mints-Android-LayeredArchitecture 暇な時に開発してるアプリのViewをすべてAnkoで書いてみました。 (まだ改善できそうなところは多い)

(追記)
HEADではもう、LayoutXMLとDatabindingを利用する形に書き換えてしまった。

ActivityやFragmentとは別のファイルにViewをまとめる

AnkoでViewを書いてる部分

class MainUi : AnkoComponent<MainActivity> {
    private lateinit var viewPager: ViewPager

    override fun createView(ui: AnkoContext<MainActivity>): View = with(ui) {
        verticalLayout {
            val tabLayout = tabLayout {
                tabMode = TabLayout.MODE_SCROLLABLE
                tabGravity = TabLayout.GRAVITY_FILL
                setSelectedTabIndicatorColor(getColor(ctx, R.color.colorPrimary))
            }.lparams(width = matchParent, height = wrapContent)

            viewPager = viewPager {
                id = 1
                offscreenPageLimit = 2
            }.lparams(width = matchParent, height = matchParent)

            tabLayout.setupWithViewPager(viewPager)
        }
    }

    fun showPages(fm: FragmentManager, pages: List<PageAdapter.Page>) {
        viewPager.adapter = PageAdapter(fm, pages)
    }
}

利用側

class MainActivity: AppCompatActivity() {
    private val ui = MainUi()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        ui.setContentView(this)
    }
}

XMLを書かなくていいのは結構いいですね。今回は一人での実装だったのでただの予想ですが、コードレビューでXMLを読むのが辛いのですが、Ankoなら多少軽減されるかも。 もっと細かい単位でAnkoComponentを実装していけばコードのreuseができそう。

MacBook Pro 13-inch with TouchBar

仕事用のMacBookPro 13inch TouchBar付きが届いたのでその感想。

tl;dr

  • 薄い軽い、持ち運ぶ気になった
  • ストロークの浅いキーボードなかなか打ちやすいぞ
  • TouchIDが最高なことはみんなしってるだろ?
  • TouchBarに最適化されたソフトウェア結構いいかも
  • TouchBarカスタマイズして便利なショートカットつくれるぞ!
  • escは常に左上にあるから困らねーぞ!
  • functionキーはfn押せばでてくるぞ!
  • キーボードだとかモニタだとかじゃらじゃらつけないんでUSB Type-Cだけなのはこまらんぞ!

ボディ

前モデルに比べて軽く、薄くなった。
多少のさだが、結構体感できるほどに違う。これは正義。
今後は通勤時にも持ち運びしてもいいかなと思う。

キーボード

MacBookとおなじようなキーボード。
慣れてみるとこれがなかなかいい。
扱い始めて数十分後にはもとのキーボードのストロークの深さが気に食わなくなった。

TouchID

TouchIDが最高なことなんてiPhone/iPadつかってる人にはわかりきったことだよね。

TouchBar

よい。 BetterTouchToolsで独自のボタンをつくれるので、AndroidStudioでよく使うコマンドを割り当てたりした。
f:id:lvla:20161218235750j:plain

ソフトウェアがTouchBarに対応している場合もなかなかおもしろい。
たとえばSafariであれば、favorites画面でbookmark barに設置しているものがTouchBarに出ていたり f:id:lvla:20161218235730j:plain メディアの再生を(safariがフォアグラウンドになくても)TouchBarで操作できたり f:id:lvla:20161218235710j:plain これ地味に便利ね。safariでなにかメディアを再生してるときに話しかけられた場合、safariにきりかえて...タブひらいて...ってやらなきゃいけなかったのが面倒だったので(面倒だったのでそもそも止めないことも多かった)

Escapeない

気にならない。
物理ボタンではなくなったが左上には常に表示されている。

Functionキーない

以前よりワンストローク増えてしまうが、fnを押せばTouchBarにFunctionキーが表示されるので困らない。
コマンド類は前述したようにTouchBarのカスタマイズで最適化できる。

TrackPad

カーソルスピード高く設定して使ってるので広くなっても特に恩恵はない。
誤タッチを気にしてるひともいるみたいだけど、ちゃんとパームリジェクション効くから落ち着けって。

とはいうものの、右下クリックをSecondaryClickに割り当ててると困る。
右下に手のひらが置かれてる状態でクリックするとSecondaryClickとして検出される。
諦めて2FingerClickでSecondaryClickにした。は〜。

USB Type-Cしかない

困らない。
外部キーボード??外部モニタ??キーボードもモニタもMBPにすでについてますよ!!
検証端末の接続もこまらない。Nexus5XはもともとUSB Type-Cです。
iPhoneはケーブルかわないとですね。は〜だるい。

電源

MagSafeはその名の通り安全で最高だった。
ただUSB Type-Cにもいいところはある。

  • 右からでも左からでも給電できる
  • バイルバッテリーから給電できる

バイルバッテリーから給電できるの最高でしょう?

総評

満足