[iOS]Bluepill1.0.0

Bluepillの1.0.0がリリースされていたのでメモ。

XCUITestサポートが少し前に完了し、1.0.0になったようですね。

https://github.com/plu/pxctest/releases

こちらに比べて、さすがに企業としての投資が違うのか、開発がBluepillの方が活発だ…

[iOS]run tests on simulators vol2

[iOS]Run multi simulators with FBSimulatorControl
にも書いている、iOSシミュレータを1つのマシンで複数動作させるやつ。最近ではLinkedInからOSSが公開されましたね。Blogはこちら。スターも多く、さすがLinkedInという感じ。

社内で FBSimulatorControl ベースの複数シミュレータで実行する環境を作ろうと思っていたけれど、もっと素晴らしいものが世に出てきててオォォォという感じ…(私、価値出せてない…)
ここら辺、実装能力が顕著に現れて私は本当にまだまだと思う一方です。。。

以下、以前からある類似のものをピックアップ。

類似

これを実施していると、正直なところ並列実行できないCloud実行環境はイマイチですね。
ローカル環境強し。。。

Architecture for GUI Testing(mobile)

In 2014, I talked about GUI testing architecture.

Recently, someone asks me about the architecture.So, I post the blog about it.

The following flow means the architecture. I think this architecture is common if anyone uses libraries such as Cucmber.

  Scenario     Abstract     Wrapper    Binding     Appium
(*.feature)   (*_steps.rb)  (*.rb)
     |           |            |           |          |
     |---------->|            |           |          |
     |           |----------->|           |          |
     |           |            |---------->|          |
     |           |            |           |--------->|
     |           |            |           |          |
     |           |            |           |<---------|
     |           |            |<----------|          |
     |           |<-----------|           |          |
     |<----------|            |           |          |
     |           |            |           |          |
  • Scenario layer
    • Describe scenarios.
    • This layer depends on “User scenarios”.
  • Abstract layer
    • Implement steps to run scenarios as Ruby code.
    • This layer absorbs the changes in scenarios.
  • Wrapper layer
    • Wrap binding.
    • This layer absorbs the changes in bindings.
  • Binding layer
    • Ruby binding

PageObject pattern is very famous. In this case, scenairo layer and abstract layer depends on pages. BTW, wrapper layer is common methods to help other layers.

[Appium]preventWDAAttachments for XCUITest strategy

Appium1.6.2から、 preventWDAAttachments というパラメータが付与されました。

これは、WebDriverAgentを動作させるときにXcodeのDerivedDataに多くの不要なファイルを書き込むことを抑制するために、そのディレクトリの権限を読み込み専用(555)に変更させるものみたいです。

defaultはtrueのようですね。

https://github.com/appium/appium-xcuitest-driver/blob/0c36c7659373c1c82a1411e50fa2503920909624/lib/desired-caps.js#L45

ただ、これはXcodeの権限設定を一部変更することになるので、capsには明記しておいたほうが良さそう。

[Android]Reactive系のテストではIdlingResourceやっぱり大事

Rx系において、Android向けの物にはRxJavaの他にAgeraがあります。
codelabsにAgeraがきていたので、簡単な学びがてらやってみました。

Rx系のテスト(Espresso使ったUI含む)までも書かれていたので、Rx系学ぶ人には良い材料になると思います。やっぱりRx系のテストコード書こうとしたら、 Espresso.registerIdlingResourcesIdlingResource の実装を使った非同期要素を待つ、ということがかけないといけないよなーということを思いました。

対象

Repositoryに対する処理

AgeraはRepositoryが1つの大事な要素だと書いていました。その中で、複雑なrepositoryを扱う場合は、以下のようになるそう。

https://codelabs.developers.google.com/codelabs/android-agera/#7

topAlbumsRepository = repositoryWithInitialValue(emptyList())
  .observe(accountManager,      // When the account changes...
           networkStatus)       // or when we get online...
  .onUpdatesPer(10000)          // but at most every 10 seconds...
  .goTo(networkExecutor)        // on the network thread...
  .getFrom(albumsFetcher)       // fetch albums from API...
  .thenTransform(                   
    functionFrom(String.class)
    .unpack(jsonUnpacker)       // unpack JSON items...
    .map(jsonToAlbum)           // for each album...
    .filter(fiveStarRating)     // filter the best...
    .thenLimit(5))              // and give us the first five of those!
  .compile();                   // Create the repository!

この中で、例えば値の変化を観測してそのないように変化があればそれをsetTextに反映する、というところは以下のようになる。

https://github.com/KazuCocoa/agera-example-android/commit/ba11829000bf820a2c34861cfb3e5c9b071a0e78

なるほど。

テストの書きやすさや保守も考えて…

このようなAgeraの特徴の他、Rx系はcallback地獄のように入れ子にコードを書けやすいが、その反面そうするとテストしにくかったり保守が困難なコードが出来上がる。そこで、Ageraの例ではそこを対応する方法を紹介している。

https://codelabs.developers.google.com/codelabs/android-agera/#9

他、処理の分散としてメインスレッド外へ処理を逃がすために、 .goTo(mExecutor) を使った処理方法も。

Espressoも使う

最後、UI含んだテストとして、 IdlingResource の実装を使った Espresso.registerIdlingResources を利用した非同期型のこのようなRx系に対するテストコードも書いています。

https://codelabs.developers.google.com/codelabs/android-agera/#11

アニメーションOFFは毎度のご愛嬌ですね 🙂

最後に

Reactive Programmingには幾つか世代があり、Ageraはまだまだ、RxJavaは2.0で世代3~4付近だと言われていました。
https://github.com/google/agera/issues/20#issuecomment-212007539

そうはいっても、Ageraを使ったこの題材は、Androidに対してReactive Programmingを入れてテスト書くまでをざっと学ぶことができるので、やることには良い意味があるものだなーと感じました。

[iOS]XCUITestを少し追って試しにシナリオを書いてみる

XCUITestがXcode7から利用できるようになったので、少し追ってみたので、メモ。

まだ正式に導入するとか、評価したとか、そういう話ではないです。

以下は、Xcode7.0で適当なXCUITestを書いたあと、コード上でメソッドを追った時の情報をもとに引用しています。Online Documentなどでは違うところがあるかも。また、 Swift をベースに追っています。

accessibility systemを使って要素を特定する

/*! Protocol describing the attributes exposed on user interface elements and available during query matching. These attributes represent data exposed to the Accessibility system. */

どうやら、XCUITestはXCTestを拡張し、さらにはAccessibilityの機構を使うみたいです。ひとまず、UIAutomationのために埋め込んだaccessibilityIdentifeirなど、そのまま流用可能のようです。

ということは、すでにUIAutomationを使いシナリオを記述している人は、テストシナリオを記述する粒度は変われど、同一のシナリオを記述することは可能かもしれないです。

例えば、 public protocol XCUIElementAttributes には

public var identifier: String { get }

が定義されています。これは accessibilityIdentifier の要素を指しているらしいので、accessibilityIdentiiferでつけた要素を特定する、ということは従来のUIAutomationと変わらず実施できます。

この要素特定には、

public func elementBoundByIndex(index: UInt) -> XCUIElement

public var allElementsBoundByAccessibilityElement: [XCUIElement] { get }

の2種類が使えそうです。見た感じ。(まだちゃんと使ってはない)

また、accessibilityベースではなく、より実装に依存することになりますが実装対象のUIButtonといったUIKitの要素単位を取得するためのインターフェースとして

public protocol XCUIElementTypeQueryProvider

が宣言されているようです。

ここら辺は、UIAutomationで取得できていた要素を、XCUITestで参照できるようにした、というもののようです。

@available(iOS 9.0, *) のある XCUI*

@available(iOS 9.0, *)

のアノテーションで指定されていうr、 XCUI* 群を使うことで、シナリオの記述などを行っていく模様。XCTestのコードを見ていると、随所で @available(iOS 9.0, *) を持った XCUI* 群を確認できます。

ここら辺では、操作を定義してたりしました。ただ、 @available をいろいろ見ていると、iOS7を対象としたものなんかもいろいろあって、XCUITestは今まで用意してきたUIAutomationだったり、accessibilityの要素をまとめあげた感じがしました。

シナリオを書いてみる

少しシナリオを書いてみました。テスト対象は新規でアプリを作成すると作られる、簡単なタスク追加のデモアプリです。その中で、navigation barのEditをタップして、Addする、というシンプルなものを書いてみました。

import XCTest

class SamplesForXCUITestUITests: XCTestCase {

    override func setUp() {
        super.setUp()
        continueAfterFailure = false
        XCUIApplication().launch()
    }

    override func tearDown() {
        super.tearDown()
    }

    func testExample() {
        let app = XCUIApplication()
        let masterNavigationBar = app.navigationBars["Master"]
        masterNavigationBar.buttons["Edit"].tap()
        masterNavigationBar.buttons["Add"].tap()
    }

}

これで、 cmd + u でXCUITestまでテストが実施されます。アニメーションなんかは従来のUIAutomationと同じ感じで、そのままUIAutomationをXCTestの一部として実施している以上の感想を持ちませんでした。

書いてみて思ったこと。

  • XCUITestはUIを見ながらシナリオを考える、というには、いささかテストコードがハードコーティングすぎる
    • まだaccessibitliyを付加して、とか試してないので、それによって印象変わるかも
    • allElementsBoundByAccessibilityElement とかで全要素取得できなかったので、ちょっと全要素取得のコツとか学ばないと、実際に書かれたコードを読む必要性が高い…
    • ひとまずRuby + Appiumのほうが同様のことをするには良い。実行速度も、Simulator上では、さほど差があるわけではない気がします。(Simulator再起動の時間は除く)
  • Espressoみたい
    • 特定のViewを直接描画する、とかできるのかな…
    • シナリオというか、操作の書き方も…
  • 操作のRecordは気軽にできてよいけれど、こういうシナリオの記述に知見がない人だとすぐにスパゲッティ作りそう
    • 開発経験を多少持っていると問題ないかな…

感想

Xcodeで accessibility* による補完を見てみると、 UIKit &gt; UIAccessibility の要素も参照していました。ここ、コードを読んでみるとiOS7とか、もっと古くからでも参照できるaccessibilityの要素にたどることができます。今までPrivate APIとして使われていた、FBが出している WebDriverAgent なんかでアクセスしているAPIを整理して、ちゃんとXCUITestのフレームワークとしてアクセスできるようにした、というのが正体なのかもしれませんね。

確かに、UIAutomationの欠点はUI操作をJavaScriptで書かないといけないという、iOS開発者にとってはなかなか酷なものでした。そこをそのままSwiftやObjective-Cで利用出来るように公式でおこなう、というだけでも、有益かもしれません。

KIFは完全にこっちに移りそうな予感がします。FBのWebDriverがこのXCUITestを経由することができるようになれば、AppiumなんかでもXCUITestの恩恵を受けられそう。

いずれにせよ、今までRuby + Appium + Android/iOSとテストを記述していたのですが、”Ruby + Appium + Android” and “XCUITest + iOS” というふうに、iOSにおいては、Ruby(などのほか言語) + Appium を経由してiOSアプリをGUI Testingするといった使い方は制限されそうな予感。次のXcode8とか、そのくらいのタイミングでUIAutomationは除かれる可能性を考えて動いたほうがよさそう。

んー。複数OSのアプリ対応を少人数で動かないといけないとき、UIAutomationが制限される未来に進む場合、iOSは制約厳しいな…

OSSとして公開された、巨大なFacebookのiOSアプリでUIAutomationを使うために使われているツール群

iOSアプリ開発、動作確認、テストを経験されている方なら分かるとおもいます。UIを確認したいときのiOSアプリの動作確認を自動化する時、UIAutomationを使う形が多いですね。そして、その場合において、一番のボトルネックはやはり実機やSimulatorの同時起動、実施速度の制約になっていきますよね。

UIAutomationは、実機で実施するには遅延があります。また、実機ではデータをCleanにして毎回テストを回すには、それ用のdebug機能を実装しなければ不可能です。

遅延に対しては、Facebookのinstruments-without-delayを経由して、遅延の少ないSimulatorで実施するこができていました。そして、特定のディレクトリを削除して毎回Cleanな状態を確保できていました。そういうことができるiOS Simulatorの価値は高いものです。(Xcode7ではツールの制約上この機能が正常に動作しませんが、このGistによると、Simulatorのplistをinjectionすれば有効にもできるそうな。)

例えば、Appiumはこのツールを組み込んでいるので、iOS実機もそうですが、シミュレータではより早く安定したGUI Testigを実施することができます。

なのですが、Simulatorは1つのセッションしか、Mac上では起動することができません。これはSimulatorやXcodeのinstrumentsに課せられた制約になります。

そこで、最近Facebookが以下の通りその問題を解決するようなライブラリをOSSとして公開しました。

FBSimulatorControl

Objective-Cにより実装されていて、複数シミュレータを起動することができるようにしているもののようです。

以下がこのOSSのAboutなのですが、このライブラリは、Private APIの DVTFoundationCoreSimulator にリンクして、直接ごにょごにょしているらしいです。例えば、シミュレータを直接操作する simctl を操作したり。Private APIをモリモリ使っているぽいので、Xcodeの変更に対してリスクはありますが、それでもFacebook同様、巨大なアプリはこういう形のツールを使わなければ、GUI Testingでは結構な時間を持っていかれますよね…

About

The original use-case for FBSimulatorControl was to boot Simulators to run End-to-End tests with WebDriverAgent. As FBSimulatorControl is a Mac OS X framework, it can be linked to from inside any Mac OS Library, Application, or xctest target. There may be additional use-cases that you may find beyond UI Test Automation.
FBSimulatorControl works by linking with the private DVTFoundation, CoreSimulator and DVTiPhoneSimulatorRemoteClient frameworks that are present inside the Xcode bundle. Doing this allows FBSimulatorControl to talk directly to the same APIs that Xcode and simctl do. This, combined with launching the Simulator binaries directly, means that multiple Simulators can be launched simultaneously. Test targets can be made that don’t depend on any Application targets, or that launch multiple Application targets. This enables running against pre-built and archived Application binaries, rather than a binary that is built by a Test Target.

また、彼らは同時期にWebDriverAgentも公開しました。これは、UIAutomation.freamwork をAppleが公開しているJavaScriptライブラリを介さず直接操作し、さらにはWebDriverのAgentとして操作を与えることをできるようにしたものです。

How it works

WebDriverAgent works under-the-hood by linking to UIAutomation.framework and calling the same APIs that are exposed through Apple’s UIAutomation.js framework.
Because it is not tied to an Instruments run, it is able to run across applications or even on the home screen. Furthermore, it’s much faster than any JavaScript UIAutomation.js driver as it runs a native HTTP server and does not need to ferry commands and results through a makeshift run loop.

AppiumはUIAutomation.jsを経由している。一方で、これはPrivateHeaderを直接取得して、その中からUIAutomaton.frameworkを直接操作してる。そのため、Javascriptのフレームワークより高速なのですね。KIFに近いかな。Appiumよりも安定していそうな感じ。

XCUITestが普通になると、WebDriverAgentはそこまで必要でなくなるかなー。という感じ。ただ、まだ先のことでしょう…
このOSS群も、必要な組織はごく一部の大きなiOSアプリを開発しているところに限られるのでしょうが…