TOKOROM BLOG

iOSとかVimとかその他日々の雑多な技術情報

Objective-Cのテストクラスからプライベートメソッド/プロパティを参照したい はてなブックマーク - Objective-Cのテストクラスからプライベートメソッド/プロパティを参照したい

Permalink

dkfjさんが Objective-Cで、プライベートメソッド・プロパティにアクセスし、ユニットテストを実行する方法 という記事を書かれていました。

せっかくなので私がやっている方法も書かせていただきます。

テスト対象のクラス

以下のようにプライベートメソッドとプロパティを持ったExampleClassをテストするとします。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#import "ExampleClass.h"

@interface ExampleClass()
- (BOOL)privateMethod;
@property (assign) BOOL flag;
@end

@implementation ExampleClass

- (BOOL)privateMethod
{
  return self.flag;
}

@end

テストクラス

自分の場合は、こんなかんじでテストクラスでテスト対象のプライベートメソッドやプロパティを宣言し直して使ってます。

GHUnitのテストカバレッジをJenkinsで表示する はてなブックマーク - GHUnitのテストカバレッジをJenkinsで表示する

Permalink

概要

設定が完了すると、JenkinsでGHUnitのテストが実行された後に以下のようにテストカバレッジが参照できます。

設定がちょっとだけ面倒ですが、一度やって慣れてしまえばなんてことありません。

実際に動かしてみたサンプルプロジェクトは Github に置いてあります。
うまく動かない場合の設定の比較などにご参照ください。

なお、ここではiOSアプリ開発用としての紹介をさせていただきます。

Objective-Cで少しでも疎結合なプログラムを書くためのチェックポイント はてなブックマーク - Objective-Cで少しでも疎結合なプログラムを書くためのチェックポイント

Permalink

チェック1. なんでもかんでもヘッダーファイルでimportしていませんか?

例えば、こんなコードを書いていませんか?

Library.hのもしかしたら改善できるかもしれない例
1
2
3
4
5
6
7
8
9
10
11
#import "OtherLibrary1.h"
#import "OtherLibrary2.h"

@interface Library

@property (strong) OtherLibrary1* otherLibrary1;
@property (strong) OtherLibrary2* otherLibrary2;

@property (assign) BOOL flag;

@end

Libraryというクラスを作り、そこでOtherLibrary1とOtherLibrary2をpublicなpropertyとする場合、当然のごとくOtherLibrary1とOtherLibrary2のimportが必要なわけですが、なにも考えずにヘッダーファイルでOtherLibrary1.hとOtherLibrary2.hをimportしてしまっていませんか?
そうしてしまうと、この Library.h をimportする全てのクラスに、OtherLibrary1.hOtherLibrary2.hも芋づる式にimportすることを強要することになってしまいます。
実際にこの Library を使うクラスでは、otherLibrary1otherLibrary2も使わないかもしれないですし、確実に使われるのでなければ、ヘッダーファイルでimportしないほうが疎結合なプログラムになります。
具体的にはこの状態だと、 OtherLibrary1もしくはOtherLibrary2が変更されると、Library.hをimportする全てのクラスにも影響が出る 状態になってしまっています。

以下、これを解消するための変更例です。

Objective-Cで独自クラスに[]でアクセスする はてなブックマーク - Objective-Cで独自クラスに[]でアクセスする

Permalink

まず、Xcode4.4から使えるObjective-Cの新しい書き方については 前の記事 をご参照ください。

Xcode4.4からMacアプリ開発で使える新しいリテラル

上記記事には書いていないのですがMac向けのアプリではこの他にも、

  • array[1] でNSArrayの要素にアクセスできる
  • dictionary[key] でNSDictionaryの要素にアクセスできる
  • @YES/@NO でもNSNumberのインスタンスを作れる

といった変更があります。
こちらについては残念ながらiOSアプリ開発ではまだ利用できません。

※ これらがターゲットがMacの場合には利用できてiOSの場合には利用できないということは、 @k_katsumi さんに教えてもらいました。

でも[]でアクセスする仕組み自体はiOSでも使えます!

いまさらですがXcode4.4からiOS開発で使えるようになった新しいObjective-Cの書き方をまとめます はてなブックマーク - いまさらですがXcode4.4からiOS開発で使えるようになった新しいObjective-Cの書き方をまとめます

Permalink

もうほとんどのかたが既知の内容と思いますが、まとめさせていただきます。
これらは全てコンパイル時に事が済む類のものなので、iOS5だけでなくiOS4でも当然使えるというのが嬉しいですね!

Xcode4.4適用前

Xcode4.3までのこのコードを…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#pragma mark - Private Category

@interface Sample ()

@property (strong) NSNumber* i;
@property (strong) NSNumber* c;
@property (strong) NSNumber* f;
@property (strong) NSArray* array;
@property (strong) NSDictionary* dictionary;

- (void)privateMethods1;
- (void)privateMethods2;
- (void)privateMethods3;

@end

#pragma mark - Main Implementation

@implementation Sample

@synthesize i = i_;
@synthesize c = c_;
@synthesize f = f_;
@synthesize array = array_;
@synthesize dictionary = dictionary_;

- (void)privateMethods1
{
  [self privateMethods2];
  [self privateMethods3];
}

- (void)privateMethods2
{
  self.i = [NSNumber numberWithInt:100];
  self.c = [NSNumber numberWithChar:'a'];
  self.f = [NSNumber numberWithFloat:3.14f];
}

- (void)privateMethods3
{
  self.array = [NSArray arrayWithObjects:@"1", @"2", [NSNumber numberWithBool:YES], nil];
  self.dictionary = [NSDictionary dictionaryWithObjectsAndKeys:@"val1", @"key1", @"val2", @"key2", nil];
}

@end

Xcode4.4適用後

Xcode4.4からはこうできます!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#pragma mark - Private Category

@interface Sample ()

@property (strong) NSNumber* i;
@property (strong) NSNumber* c;
@property (strong) NSNumber* f;
@property (strong) NSArray* array;
@property (strong) NSDictionary* dictionary;

@end

#pragma mark - Main Implementation

@implementation Sample

- (void)privateMethods1
{
  [self privateMethods2];
  [self privateMethods3];
}

- (void)privateMethods2
{
  self.i = @100;
  self.c = @'a';
  self.f = @3.14f;
}

- (void)privateMethods3
{
  self.array = @[@"1", @"2", @(YES)];
  self.dictionary = @{@"key1": @"val1", @"key2": @"val2"};
}

@end

以下、具体的な内容を説明します。