EXC_BAD_ACCESSのユニットテストはどのようにして行うのですか?

EXC_BAD_ACCESSの問題を解決する方法はわかっていますが、単体テストの方法はわかりません。単にクラッシュするのではなく、コードでEXC_BAD_ACCESSをキャプチャする方法はありますか?

ここで私が尋ねる理由は次のとおりです。ブロックを頻繁に使用するライブラリを作成しました。

- (void)doSomething:(void (^)())myBlock;

私の doSomething:の実装では、次のようにブロックを実行します。

myBlock();

呼び出し側がブロックに対してnilを渡すと、 EXC_BAD_ACCESS でクラッシュします。そのため、解決策はブロックが存在するかどうかをチェックすることです。

if (myBlock) {
    myBlock();
}

このnilチェックは忘れやすいので、クラッシュが発生したときに失敗する単体テストを書く方法が欲しいです。クラッシュはテスト失敗と見なすことができると思いますが、クラッシュではなく素晴らしい失敗メッセージを表示するためにテストを実行しようとしている人にとっては、より良いと思います。何か案は?

9

2 答え

私はあなたがサブプロセスでテストを実行する必要があると思います。サブプロセスをクラッシュさせ、そのクラッシュをチェックし、テストが正常に実行された場合は失敗するようにすることができます。

Peter Hoseyのシングルトンテストコードから作業してください。

- (void) runTestInSubprocess:(SEL)testCmd {
        pid_t pid = fork();
       //The return value of fork is 0 in the child process, and it is
       //the id of the child process in the parent process.
        if (pid == 0) {
           //Child process: run test
           //isInSubprocess is an ivar of your test case class
            isInSubprocess = YES;
            [self performSelector:testCmd];
            exit(0);
        } else {
           //Parent process: wait for child process to end, check 
           //its status
            int status;
            waitpid(pid, &status, /*options*/ 0);
           //This was a crash; fail the test
            STAssertFalse(WIFSIGNALED(status), @"Test %@ crashed due to signal %d", NSStringFromSelector(testCmd), WTERMSIG(status));
        }
}

各テストは、次のようなサブプロセスで実行されます。

- (void) testSomething {
    if (!isInSubprocess) {
           //Hand off this test's selector to be run in a subprocess
            [self runTestInSubprocess:_cmd];
            return;
    }

   //Put actual test code here
    STAssertEquals(1, 1, @"Something wrong with the universe.");

}

これを調整する必要があるかもしれません。私はそれをテストしていない。

4
追加された
ooh、ええ、私は fork()がiOSでも利用できるのかどうか分かりません。これを完全に正しく機能させるのが難しいです。私が何かを明らかにするなら、私は更新します。
追加された 著者 Josh Caswell,
これは非常に面白いです。最初の試みでは、私が何をしてもテストに失敗しますが、このプロジェクトはCocoa Touchの静的ライブラリの対象となり、テストを実行するとiPhone Simulatorが起動します。私はfork()がそこで動作するとは思わないので、Mac/Cocoaのターゲットとしてこれを試してみて、何が起こるか見てみましょう。ありがとう!
追加された 著者 greenisus,

I would suggest using one of the assertion macros found in the Assertions and Logging Programming Guide

だからあなたは次のようなことをすることができます:

NSAssert(myBlock != nil, @"myBlock must not be nil")

これにより、メソッドが実行を継続する前に満たされなければならない前提条件が強制されます。また、アプリをクラッシュさせることができ、EXEC_BAD_ACCESS以外の理由が表示されます。

1
追加された
これは単体テストではあまり役に立ちません。 greenisusには、 if(myBlock){myBlock()}; も同様に問題に記載されているかどうかを確認できます。問題は、私が完全に誤解していない限り、テスト中のコードのブロックをチェックする忘れるを検出する単体テストを書く方法です。
追加された 著者 Josh Caswell,
ジョシュは正しい。私はそのアサーションをうまく使うことができますが、実際にテストしようとしているのは、myBlockがnilであるかどうかは関係ありませんので、メソッドをnilブロックに渡すとクラッシュしないということです。
追加された 著者 greenisus,
それは私が求めていることではありません。私はnilをmyBlockの許容値にしたいので、nilを渡してもクラッシュが発生しないことを確認するテストを探しています。
追加された 著者 greenisus,
アサーションを使用することにより、 myBlock がnilでないことが保証されます。単体テストでそのメソッドをテストする場合は、アサーションが失敗したためにテストが失敗します。
追加された 著者 Brandon A,
ゴッチャ。私はそれを認識しませんでした。
追加された 著者 Brandon A,