2018年8月6日月曜日

Kotlin メモ : padStart, padEnd

padStart(length: Int, padChar: Char = ' ')
padEnd(length: Int, padChar: Char = ' ')

CharSequence と String の拡張関数として用意されています。

length で指定した長さに足りない場合、padChar で start/end を埋めたものを返します。
padChar を省略した場合、' ' が使われます。

assertThat("a".padStart(3)).isEqualTo(" a") assertThat("aa".padStart(3)).isEqualTo(" aa") assertThat("aaa".padStart(3)).isEqualTo("aaa") assertThat("aaaa".padStart(3)).isEqualTo("aaaa") assertThat("a".padStart(3, '-')).isEqualTo("--a") assertThat("aa".padStart(3, '-')).isEqualTo("-aa") assertThat("aaa".padStart(3, '-')).isEqualTo("aaa") assertThat("aaaa".padStart(3, '-')).isEqualTo("aaaa") assertThat("a".padEnd(3)).isEqualTo("a ") assertThat("aa".padEnd(3)).isEqualTo("aa ") assertThat("aaa".padEnd(3)).isEqualTo("aaa") assertThat("aaaa".padEnd(3)).isEqualTo("aaaa") assertThat("a".padEnd(3, '-')).isEqualTo("a--") assertThat("aa".padEnd(3, '-')).isEqualTo("aa-") assertThat("aaa".padEnd(3, '-')).isEqualTo("aaa") assertThat("aaaa".padEnd(3, '-')).isEqualTo("aaaa")

2018年8月1日水曜日

Android で Dagger を使う(その4 : @BindsInstance)

Android では @Provides の引数として Application が必要だったり、@Inject で Application を渡したいことがあります。

Module のコンストラクタで Application のインスタンスを渡すようにすることで実現できますが、もっといい方法があります。

まず Component に @Component.Builder をつけた Builder インターフェースを用意します。
インタフェースの中に @BindsInstance アノテーションをつけたメソッドを用意し、Application インスタンスを渡せるようにします。 @Component( modules = [ AppModule::class ] ) internal interface AppComponent { @Component.Builder interface Builder { fun build(): AppComponent @BindsInstance fun application(application: Application): Builder } ... } あとは Component を構成するときに、用意したメソッドで Application インスタンスを渡すだけです。 class MyApplication : Application() { override fun onCreate() { super.onCreate() val appComponent = DaggerAppComponent .builder() .application(this) .build() ... } } 生成されたコードでは DaggerAppComponent が application インスタンスを保持し、MembersInjector や Factory に適宜渡しています。


2018年6月20日水曜日

LiveData を UnitTest でテストする

デザートの文字列を保持して、追加・削除されたタイミングで保持数を LiveData で通知する DessertsHolder を UnitTest でテストしてみましょう。 class DessertsHolder { private val counter = MutableLiveData<Int>() private val list = mutableListOf<String>() init { counter.value = 0 } fun getCounter(): LiveData<Int> = counter fun add(item: String) { list.add(item) counter.value = list.size } fun remove(item: String) { list.remove(item) counter.value = list.size } fun clear() { list.clear() counter.value = 0 } } 特に何もせず次のような普通の UnitTest を書くと、Looper が mock されていないというエラー(RuntimeException: Method getMainLooper in android.os.Looper not mocked. )がでます。 class DessertsHolderTest { @Test fun test() { val holder = DessertsHolder() holder.add("Donuts") assertThat(holder.getCounter().value).isEqualTo(1) } }

そこで、まず AAC の core-testing ライブラリを追加します。 dependencies { def lifecycle_version = "1.1.1" testImplementation "android.arch.core:core-testing:$lifecycle_version" }
そして @get:Rule で rule に InstantTaskExecutorRule を指定します。このとき get: をつけないと rule が public ではないという ValidationError (ValidationError: The @Rule 'rule' must be public.)が起こるので注意しましょう。 class DessertsHolderTest { @get:Rule val rule: TestRule = InstantTaskExecutorRule() @Test fun test() { ... } }