予定がないのでiOSにおけるユニットテストについてまとめる その1

  • 115
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

そもそものユニットテストって?

プログラムを構成する比較的小さな単位(ユニット)が個々の機能を正しく果たしているかどうかを検証するテストです。通常、関数やメソッドが単体テストの単位(ユニット)となります。

今のところなんとなくメソッド、クラスが実装した通りに動くことを証明するもの。というイメー
ジ。iOSではメソッドが実装通り動いても、振る舞いが予想通りいかないことが往々にしてあるので、これだけやっておけばいいわけじゃない。

書くことのメリット

これにはいろいろな意見があった。

  • コードの変更を許容しやすくなる
  • コードの理解を助ける
  • 実装者の意図しない変更を防ぐ
  • コードを書くことを意識して書くと、ほどよくメソッドがほぐれる

確かに振り返ってみても、コードを読んでて「ここどんな値が来るんだろうな〜。せや、ブレークポイント使ってウォッチしよ!」とかってことよくあった。Xcodeならまだしも、PHPとかRubyとかスクリプト系の言語だとデバッグするのもめんどくさい。
テストコードがあって、入出力系のテストコードがあれば理解の助けになるなとも思った。

ふむふむ。

*参考
ユニットテストを書こう!
ユニットテストはなぜ必要なの? (1/2)

よくある間違い

  • ユニットテストを導入するならば、実装コストの2倍以上を見積もるべきです。
  • ダメなテストコードは結局品質も担保できない
  • ちゃんと継続的に使和なければ意味がない

2倍以上。たしかに。頑張って書いてみたら大変だった。
テストコードを書いているとプロダクションコードも直さなければってなったりもするし、
最初からちゃんとテストを意識して実装していくことが非常に大事

*参考
ユニットテストにまつわる10の勘違い

iOS(Xcode)におけるユニットテスト

Xcodeユニットテストガイドをちょっと読んでみたが古い。
Xcode4時代のメソッドで書かれている。STAssertなんたらというのは古いようだ。
Xcode5からはXCTestというのが使える。プロジェクトにも初めからtestが履いていてCmd+Shift+Uを押すだけで動いてくれる。

XCTest

import UIKit
import XCTest

class UnitTestTests: XCTestCase {

    override func setUp() {
        super.setUp()
        // Put setup code here. This method is called before the invocation of each test method in the class.
    }

    override func tearDown() {
        // Put teardown code here. This method is called after the invocation of each test method in the class.
        super.tearDown()
    }

    func testExample() {
        // This is an example of a functional test case.
        XCTAssert(true, "Pass")
    }

    func testPerformanceExample() {
        // This is an example of a performance test case.
        self.measureBlock() {
            // Put the code you want to measure the time of here.
        }
    }

}

このようなテストコードが最初から用意される。
ボタン押すだけでテスト単位で実行されるし、success/failのログが横にポチッと残っているのでわかりやすい。
スクリーンショット 2015-08-08 13.49.34.png

Testコードの書き方

PHPUnitもJUnitもなんかしらのユニットテストクラスを継承して、test*というプレフィックスで関数を用意すればいい。
PHPUnit
XCTestは歴史が浅いので、テストの例とかはPHPの方がいっぱいあって勉強になるかも・・・

テストコードを読む

PHPのほうがええんやないか〜と思っていたが、信頼あるiOSのライブラリたちはちゃんとテストコードが書かれている。

AFNetworking

AFNetworkingはしっかり書いてあった。メソッド名がtestThatAFHTTPRequestSerialiationSerializesQueryParametersCorrectlyとかアンチObjCの人にまたディスられそうなメソッドが散見されるが・・・
https://github.com/AFNetworking/AFNetworking/blob/master/Tests/Tests/AFHTTPRequestSerializationTests.m#L68

BlocksKit

ラップされたBlocksメソッドを提供するBlocksKitはメタプログラミング的なテストコードを書いていた。
https://github.com/zwaldowski/BlocksKit/blob/master/Tests/A2BlockInvocationTests.m#L116

SDWebImage

SDWebImageをみていたら見慣れないメソッドがあった
https://github.com/rs/SDWebImage/blob/master/Tests/Tests/SDImageCacheTests.m#L50

expect([self.sharedImageCache diskImageExistsWithKey:kImageTestKey]).to.equal(NO);

なんだろうと調べたらexpectaというのを使っていた。
ユニットテストにもライブラリはいろいろあるようだ。

iOSにおけるテストコードのライブラリ

Expecta

http://qiita.com/naokits/items/2f1ec5f4e09832fda1c7

ExpectaはGithubのメンバーが作ったマッチャーです。BDDフレームワークであるSpecta(これもGithub製)と組み合わせて使用すると、手軽にテストコードが書けます。特に非同期テストの書きやすさは秀逸です。是非皆さんも一度使ってみることをお勧めします。

specta

http://qiita.com/yuki_okawa/items/1586375581ea8cc510fa
Cocoapodsでも推奨されているそうだ。
matcherにはexpectaを使っている。

そもそもマッチャーってなんだろうな

JUnitには以下のようなものがある。
登場するマッチャー
・等価であるか:is()
・等価でないか:not()
・nullであるか:nullValue()
・nullでないか:notNullValue()
・指定したインスタンスか:instanceOf()
・同じインスタンスか:sameInstance()
・全ての条件を満たすか:allOf()
・いずれかの条件を満たすか:anyOf()
・値は何でもよい:anything()
・トレースに表示する文字列を変更:describedAs()

OCMock

http://safx-dev.blogspot.jp/2012/03/ocmock.html
http://ocmock.org/

そもそもモックとは?

外部APIとかStrutsのDynaActionFormとか、単純にインスタンス化できないものをMock化してテストできる

Javaの単体試験におけるモックの活用メモ EasyMock、PowerMock

ということす。利用方法としては以下のように仮想的にインスタンスを作ってレスポンスのモックを作れる。

// Mock生成
id mock = [OCMockObject mockForClass:[SampleClass class]];
[[[mock stub] andReturn:True] someMethod:@"FOOBAR"];

// Test
XCTAssert([mock someMethod:@"FOOBAR"], @"OK");

こうやってみるとめちゃんこ便利だ。
まえまでこのロジックはプロダクションコードに埋め込んでいたこともあったけど、全部テストコード側にもっていける。もちろん、APIであればレスポンスがちゃんとこのモックと一致している必要もある。
そこもテストコードでカバーすればいい。

*参考
Objective-Cのテストでのモックとかスタブとか差し替えの話

テスト取り巻く考え方

継続的インテグレーショ・・・うう、頭痛が。
知ってはいるつもりだし、事例もよく聞くし、いろいろなツールがあることもしているけど、
iOSって視点ではどうなんだろう。

その2へ
http://qiita.com/jumbOS5/items/93233fbdadf6abe58300

まとめ

iOSはユニットテストの仕組みがすでにあるし、ライブラリもある。
それをつかったコードもOSSを見れば勉強になるし、導入はしやすい。
継続的に回すための仕組みはTravisCIやJenkinsがカバーしてくれる、
あとは有能なエンジニアがテストコードを書ければ品質をあげることができるはず。

というのは机上の話で、「Modelだけ一応書いている」とか「書いていたけど最近動いていない」とかっていうのもよく聞く。
ただテストを意識して書くことっていうのは本当に大事だし、仕様が複雑になるので最低限はテストコードでカバーしようっていうのを頭の片隅において胃置くだけでも全然違うんじゃないかなと思った。
いい休日になった。