- 投稿日:2019-03-01T23:46:31+09:00
[Flutter] Kotlin/Swift/C/C++ をモックせずにテストする
基本的にはできない/しない もの
Flutter では、3種類の Test を書くことでコードの品質を保つことができる。
そのうち、実機/エミュを使って実行されるのが Integration Test。
このテストは、目/スクリーン の感覚で品質をテストするものであり、これを書く時 platform 依存のロジックは setMockMethodCallHandler を使ってモックするのが基本です。じゃあどうするか?
Integration Test は実機/エミュで実行されるため、 platform 依存のメソッドを呼び出すことは可能。
ただし、Integration Test の test 側のファイルでは、アプリの使用するファイルを import することはできない。
そこで、Test 用 Widget を生成する側のファイルにテストしたい処理を挿し込むことで実現しました。分かりにくいと思うので例を見て下さい。
例
例えば、 C/C++ で実装したロジックを Kotlin/Swift でラップし、さらにそれを Dart でラップした OthelloEngine クラスがあるとします。
それのロジックを モックせずにテストしたい ということ。Test 用 Widget を生成する側のファイル に処理を挿し込む。こんな感じ。
test_driver/main.dart// import 文は省略 void main() { // See: https://flutter.io/testing/ for more info. enableFlutterDriverExtension(); // ***これを追加*** othelloEngineTest(); runApp( // Widget のあれこれ ); } // platform 依存のロジックを含む処理 void othelloEngineTest() { final othelloEngine = OthelloEngine() ..echo() ..initialize() ..play('f5f6') ..move('f7') ..getDisc(0).then((disc) { if (disc != 5) { exit(0); // やべー } }); }test_driver/main_test.dart// import 文は省略 void main() { group('foo bar test', () { FlutterDriver driver; setUpAll(() async { driver = await FlutterDriver.connect(); }); tearDownAll(() async { if (driver != null) { await driver.close(); } }); test('foo bar ; verify foo bar', () async { // 色々テスト }); }); }そんで、こんな感じのコマンドで実行すれば OK
$ flutter drive --target=test_driver/main.dart --flavor=developmentおわりに
しょうがないのかなぁ。。。と思っています。
Swift/Kotlin のレイヤーでテスト書いてもいいんですが、それをラップした Dart レイヤーでテストしたいってなりますよね....ちなみに、Kotlin/C/C++ のデバッグ方法は この記事 にまとめておきました。
- 投稿日:2019-03-01T23:30:50+09:00
Kotlinで美しきFizzBuzzを書く
Kotlinにも拘わらず、Javaと同じようにFizzBuzzを解くプログラムをあるブログで見ました
というわけでKotlinの言語機能を活かして美しきFizzBuzzを書いて見たいと思います
ただ、StringBuilderありとなしで結構変わるので今回は2つに場合分けして教えたいと思いますKotlinらしくないコード(StringBuilderを使わない場合)
fun main(args: Array<String>) { for (num in 0..100) { if (num % 15 == 0) println("FizzBuzz") else if (num % 3 == 0) println("Fizz") else if (num % 5 == 0) println("Buzz") else println(num) } }引数を取らない場合は
args: Array<String>を省略できるfun main() { for (num in 1..100) { if (num % 15 == 0) println("FizzBuzz") else if (num % 3 == 0) println("Fizz") else if (num % 5 == 0) println("Buzz") else println(num) } }これは多分殆どの人が知ってると思います
if文をExpressionとして使う
fun main() { for (num in 1..100) { println( if (num % 15 == 0) "FizzBuzz" else if (num % 3 == 0) "Fizz" else if (num % 5 == 0) "Buzz" else num ) } }これはあまり綺麗ではありませんが、Kotlinでの必須テクであるので一応載せました
後述するwhen文と合わせるともうすこし綺麗にできますwhen文を使う
fun main() { for (num in 1..100) { when { num % 15 == 0 -> println("FizzBuzz") num % 3 == 0 -> println("Fizz") num % 5 == 0 -> println("Buzz") else -> println(num) } } }見た目の美しさが全然違う!
やっぱりif...else-if...を延々と書かなくて済むのは楽ですね。ただしif...if...は作れないので注意です。fun main() { for (num in 1..100) { println( when { num % 15 == 0 -> "FizzBuzz" num % 3 == 0 -> "Fizz" num % 5 == 0 -> "Buzz" else -> num } ) } }ちなみにwhen文もExpressionとして利用可能です
まあ、正直さっきのほうが見やすい気もしますが一応Kotlinらしくないコード(StringBuilderを使う場合)
fun main() { for (num in 1..100) { val builder = StringBuilder() if(num % 3 == 0) builder.append("Fizz") if(num % 5 == 0) builder.append("Buzz") if(builder.isEmpty()) builder.append(num) println(builder) } }buildString関数を使う
fun main() { for (num in 1..100) { println(buildString { if (num % 3 == 0) append("Fizz") if (num % 3 == 0) append("Buzz") if (isEmpty()) append(num) }) } }buildString関数めっちゃ便利です。どんなプログラムでもよく使います
でもこれだとbuildStringがprintln関数の引数と使われているので、}の後に)があるのが少しきたないですねforEachを使って書き換える
fun main() { (1..100).map { buildString { if (it % 3 == 0) append("Fizz") if (it % 5 == 0) append("Buzz") if (isEmpty()) append(it) } }.forEach(::println) }今までに書いてきた1..100の実体はIntRangeです。これとfor文を組み合わせるのが最も一般的ですが、StreamAPIとその拡張をつかってモダンに書くこともできます
ちなみに::printlnはメソッド参照です。メソッドの引数が要求される引数と一致するときに使うことができます。これ以外のメソッド参照の使い方としては、例えばJavaの一部ライブラリとかだとクラスを要求されることがあるので、そういう時KotlinではSomeClass.classとはできないのでSomeClass::class.javaとします
余談ですが、プロパティを読み込むときとかはval properties = Properties() val file = File(filePath) file.inputStream().use(properties::load)みたいにすると三行で読み込むことができます。便利!!
- 投稿日:2019-03-01T07:46:33+09:00
ProgressTimeLatchとは何なのか
DroidKaigiのカンファレンスアプリのソースコードを見ていたら、
ProgressTimeLatchなるものが使われており、何だろうと思い調べてみると、色々と便利なやつだったので投稿します。端的にいうと、プログレスバーなどを使っている時に起こるチラつきを、いい感じに制御してくれるやつでした。
ProgressTimeLatchとは
調べてみると、下記のツイートで紹介されたのが最初のようです。(おそらく)
Chris Banes氏と言えば、色々ライブラリを公開されており、お世話になった方も多いのではないかと思います。
https://twitter.com/chrisbanes/status/927928428539580416
Handy class I've just wrote: ProgressTimeLatch. It's basically ContentLoadingProgressBar but works with any view https://t.co/qaMKi9ctxm
— Chris Banes (@chrisbanes) 2017年11月7日ここで
ContentLoadingProgressBarなるもの出てきますが、そもそもProgressTimeLatchはContentLoadingProgressBarの機能をどんなViewでも手軽に使えるように作られたもののようです。ちなみに
ContentLoadingProgressBarはSupport Libraryに入っているらしいです。(知らなかった…)
https://developer.android.com/reference/android/support/v4/widget/ContentLoadingProgressBarどう便利なのか
アプリを作っていると、下記のようなことがあるかと思います。
- API通信などの非同期処理をする。
- 処理をしている間は、プログレスバーなどを表示して、処理中を表す。
- 処理が終了したら、プログレスバーなどを非表示にする。
あるあるの処理だと思います。
しかしながら、この処理の小さな問題として、APIの通信が一瞬で終わった場合は、プログレスバーの表示・非表示が一瞬で行われ、ユーザーとしては何かがチラついたように感じることがあるかと思います。
これをいい感じに制御してくれるのが
ProgressTimeLatchというわけです。使い方
かなりシンプルなクラスなので、ソースコードを読めばなんとなくわかるかと思いますが、軽く解説。
( ソースコードはこちら→ https://github.com/chrisbanes/tivi/commit/96e7cae7560ffd358b8c58c47267ed1024df53f6 )class ProgressTimeLatch( private val delayMs: Long = 750, private val minShowTime: Long = 500, private val viewRefreshingToggle: ((Boolean) -> Unit) )コンストラクタは上記のような感じになっています。
delayMs
Viewを表示するまで遅延させる時間をセットします。
minShowTime
Viewの最低限の表示時間をセットします。
viewRefreshingToggle
Viewの表示・非表示のLambdaをセットします。既存のコードに適用する場合は、こちらのdiffが参考になりそうです。対象のViewをラップするようにして、今まで表示・非表示を切り替えていたところで、
refreshingを切り替える形。いい感じに制御してくれる例
下記のような場合を想定します。
ProgressTimeLatch(delayMs = 500, minShowTime = 500) { // ProgressBarの表示・非表示 }・非同期処理が早く(100ms)終わった場合
→delayMsより小さいのでProgressBarは表示されない(チラつかない)・非同期処理がやや早く(600ms)終わった場合
→delayMsより大きいのでProgressBarは表示される
→minShowTimeが残っているので、残り時間分表示して非表示になる(チラつかない)・非同期処理が遅く(2000ms)終わった場合
→delayMsより大きいのでProgressBarは表示される
→delayMsとminShowTimeの合計以上なので、元の処理が終わったタイミングで非表示になる(チラつかない)という感じです。便利!
まとめ
ProgressTimeLatchはチラつきをいい感じに制御してくれるやつだった- 使い方もシンプルなので、使っていきたい