はじめに
仕事では、今までは新規開発だった。なので、真っ白なテキストからテストを書くことができた。しかし、これからは、流用開発。既存コードに機能追加しなければならない。そしてその既存コードはうんざりするほどの量があり、かつテストは1つもない。。。
1つのファイルは5000行くらいあって、インクルードはし放題。インターフェイスもごちゃまぜな感じ。そもそも、ヘッダファイルがソースコードごとにない!テストを通そうにも、うんざりするほどのUndef関数を作成しなければならず、実行ファイルも自力では作れなさそうだ。。。
そんなゲームオーバーで泣きたくなるような状況でも、なんとかテスト環境を構築する方法を模索していた。Undef関数をヘッダファイルから自動生成するツールがどうしても欲しかった。そこで、いろいろと探した所、“CMock"がよさそうだったので、今日は遊んでみた。
CMockとは
CMockとは、C言語用のモッキングフレームワーク。
Throw The Switch! - White Papers - CMock Intro
以下、概要を日本語訳。
CMock はヘッダファイルからモックインターフェイスを生成する小粋なツールです。CMockによって依存関係のあるモジュールをより簡単にUnitTestすることができます。
int DoesSomething(int a, int b);
自動的にDoesSomething関数が生成され、実際のDoesSomething関数の代わりにリンクすることができます。このモック化されたものをつかうことによって、期待したデータをモジュールが受け取ったかを検証することができ、モジュールからどんなデータでも望むように返すことができます。あなたが欲しいエラーを返すことだって、もっともっと・・・隣接する実際の最新モジュールの振る舞いはなんだって作り出し、瞬時にしてあなたは以下のような力を得るのです。
“オレは作った出来立てのモジュールのあらゆる細部をコントロールし、検証できるのだ!"
このことをより簡単にするために、CMockでは以下のような関数郡も用意しています。なのでテストごとに、その生成されたDoesSomething関数に、どのように振る舞うのかをきけばよいのです。
void DoesSomething_ExpectAndReturn(int a, int b, int toReturn); void DoesSomething_ExpectAndThrow(int a, int b, iEXCEPTION_T error); void DoesSomething_StubWithCallback(CMOCK_DoesSomething_CALLBACK YourCallback); void DoesSomething_IgnoreAndReturn(int toReturn);
これらの関数を次々に重ねあわせていくことで、あなたがなにを検証したいのかを意識させます。こんなふうに、
test_CallsDoesSomething_ShouldDoJustThat(void) { DoesSomething_ExpectAndReturn(1,2,3); DoesSomething_ExpectAndReturn(4,5,6); DoesSomething_ExpectAndThrow(7,8, STATUS_ERROR_OOPS);
CallsDoesSomething( ); }
このテストはCallsDoesSomethingを呼びます。これが、われわれがテストしたい関数です。この関数がDoesSomethngを3回呼ぶことを期待しています。初めは、DoesSomething(1,2)で呼ばれることを確かに確認し、魔法のように3を返します。二回目は、DoesSomething(4,5)で呼ばれることを確認し、6を返します。3回めはDoesSomething(7,8)で呼ばれて値の代わりにエラーを投げます。もしCallsDoesSomethingがこの通りでないならば、テストは失敗します。DoesSomethingを呼ばなくても、呼びすぎても、また間違った引数でよんでも、間違った順番で読んでも、テストは失敗します。
CMockはUnityをベースにしています。そしてそれは、全ての内部でのテストに使われています。CMockは全ての作業でRubyを使います(version 1.8.6 - 1.9.2)
CMockのダウンロード
CMockのダウンロードは以下のサイトから。
CMock プロジェクト日本語トップページ - SourceForge.JP
githubでも公開されている。
$ git clone git://github.com/ThrowTheSwitch/CMock.git .
(補足)git cloneだと、vendor配下にあるunityとc_expectionがダウンロードできなかった。それぞれを追加で持ってくる。
$ git clone git://github.com/ThrowTheSwitch/Unity.git vendor/unity $ git clone git://github.com/ThrowTheSwitch/CException.gitvendor/c_exception/ vendor/c_expeption
CMockをとりあえず動かしてみる
事前にRuby と rakeをインストールしておくことが必要。
examples配下で実行
とりあえず、ちゃんとダウンロードできたかどうかを確認するために、examples配下に移動して以下を実行。(mocksとbuildディレクトリをつくっておかないとエラーした(´・_・`))
$ cd examples/ $ mkdir mocks $ mkdir build $ rake
それっぽいモック生成メッセージとテストが走ってやや感動するはず。最後までテストが通らなかったけれども、とりあえずよしとしよう。
cmock配下で実行
つづいて、cmockインストールディレクトリでrakeを実行。
$ rake rake aborted! cannot load such file - rspec/core/rake_task
rspecがないよといわれるのでrspecをインストールする。
$ gem install rspec
再度rakeを実行すると以下のエラー。
/usr/lib/ruby/1.9.1/rubygems/custom_require.rb:36:in `require': cannot load such file - hardmock (LoadError)
hardmockをインストールする。
$ gem install hardmock
再度rakeを実行て、それっぽいテストが動作した。ヤタ───v(-∀-)v───♪
CMockの使い方
これも、原文をそのまま日本語訳してしまう。
CMockはRubyスクリプトとクラスです。なので、コマンドラインから直接リヨ出来ます。また、自分のスクリプトやrakeファイルにインクルードできます。
コマンドラインからモックする
CMockを解凍したら、'lib'ディレクトリ内にCMock.rbがある。これが動作させるものです。モックするためには、ヘッダファイルのリストが必要です。それとともに、より詳細な構成をしていするためには、オブションとしてyamlファイルが必要です。(configration optionの章を参照)
たとえば、これは構成をMyConfig.ymlで指定して、3つのモックを生成する。
ruby cmock.rb -oMyConfig.yml super.h duper.h awesome.h
これは、デフォルト構成で2つのモックを生成する。
ruby cmock.rb ../mocking/stuff/is/fun.h ../try/it/yourself.h
スクリプトやRakeからモックする
CMockはスクリプトやRakeファイルから直接利用できる。cmock.rbをインクルードして、CMockの実装を生成する。実装を生成したら、3つのうちのいずれかの方法で初期化する。
なにも指定しなければ、デフォルトの設定で動作させることになる。 cmock = CMock.new
YAMLファイルを指定することで、好きな構成オプションを指定できる。 cmock = CMock.new('../MyConfig.yml')
例外として、オプション指定可能。 cmock = Cmock.new(:plugins => [:cexception, :ignore], :mock_path => 'my/mocks/')
既存コードにcmockを組み込んで実行する
ここからが本題。一つのファイル(モジュール)をテストしたい時、他のファイルが存在しなくてもテストを実行するために、Undef関数をcmockで自動生成したい。
cmockと同じgithub上にあった、以下のサンプルを利用する。
git clone git://github.com/ThrowTheSwitch/CMockExample.git .
src/LedControl.cをテストしたいとする。普通にコンパイルしようとすると、当然怒られる。
$ gcc src/LedControl.c /tmp/ccsR1nMW.o:LedControl.c:(.text+0x14): undefined reference to `_GPIO_SetPin' /tmp/ccsR1nMW.o:LedControl.c:(.text+0x28): undefined reference to `_GPIO_SetPin' /tmp/ccsR1nMW.o:LedControl.c:(.text+0x68): undefined reference to `_GPIO_ClearPin' /tmp/ccsR1nMW.o:LedControl.c:(.text+0x7c): undefined reference to `_GPIO_ClearPin' /usr/lib/gcc/i686-pc-cygwin/4.5.3/../../../libcygwin.a(libcmain.o): In function `main': /usr/src/debug/cygwin-1.7.17-1/winsup/cygwin/lib/libcmain.c:39: undefined reference to `_WinMain@16' collect2: ld はステータス 1 で終了しました
モックを生成する。モックを生成するためのディレクトリを作成して、コマンド実行。
$ mkdir mocks $ ruby ../lib/cmock.rb src/Gpio.h src/main.h src/System.h Creating mock for Gpio Creating mock for main Creating mock for System $ ls mocks/ MockGpio.c MockGpio.h Mockmain.c Mockmain.h MockSystem.c MockSystem.h
おお、感動的だ。゚(●'ω'o)゚。
ココからが独自カスタマイズ。必要な部品をunityプロジェクト、cmockプロジェクトからカレントディレクトリにかき集めてくる。
- unity/src/unity.c unity.h unity_internals.h
- unity/auto/*
- cmock/lib/*
- cmock/config/*
- cmock/src/cmock.c cmock.h
rakeをよく知らんので、Makefileもちょろっと書いてmakeしてみる。Makefileって自動生成できないのかな。。
>
> <p>
> このままだと、mocks/Mock****というモックオブジェクトが生成される。test_***と結合しようとするとエラーしたので、lib/CMockConfigでmock_prefixを変更する。
> </p>
>
> <blockquote>
> <p>
> :mock_prefix => 'mock_',
> </p>
> </blockquote>
>
> <p>
> 再度実行してみる。
> </p>
>
> <blockquote>
> <p>
> <font size="1">$ make rm -f build/*.o ; rm -f LedControl.exe ; mkdir -p build </p>
>
> <p>
> ruby auto/generate_test_runner.rb test/test_LedControl.c build/test_LedControl_Runner.c
> </p>
>
> <p>
> ruby lib/cmock.rb src/Gpio.h
> </p>
>
> <p>
> Creating mock for Gpio
> </p>
>
> <p>
> ruby lib/cmock.rb src/main.h
> </p>
>
> <p>
> Creating mock for main
> </p>
>
> <p>
> ruby lib/cmock.rb src/System.h
> </p>
>
> <p>
> Creating mock for System
> </p>
>
> <p>
> gcc -Isrc -Imocks -DTEST -DUNITY_SUPPORT_64 src/unity.c src/cmock.c src/LedControl.c test/test_LedControl.c build/test_LedControl_Runner.c mocks/mock_Gpio.c mocks/mock_main.c mocks/mock_System.c -o LedControl.exe
> </p>
>
> <p>
> ./LedControl.exe
> </p>
>
> <p>
> test/test_LedControl.c:13:test_LedControl_TurnLedOn_should_turn_on_GPIO_pin_1_when_turning_on_the_red_LED:PASS
> </p>
>
> <p>
> test/test_LedControl.c:22:test_LedControl_TurnLedOn_should_turn_on_GPIO_pin_2_when_turning_on_the_blue_LED:PASS
> </p>
>
> <p>
> test/test_LedControl.c:31:test_LedControl_TurnLedOff_should_turn_off_GPIO_pin_1_when_turning_off_the_red_LED:PASS
> </p>
>
> <p>
> test/test_LedControl.c:40:test_LedControl_TurnLedOff_should_turn_off_GPIO_pin_2_when_turning_off_the_blue_LED:PASS
> </p>
>
> <p>
> --------
> </p>
>
> <p>
> 4 Tests 0 Failures 0 Ignored
> </p>
>
> <p>
> OK</font>
> </p></blockquote>
>
> <p>
> <font color="#0000ff" size="5">。゚(●'ω'o)゚。おお、感動的だ。゚(●'ω'o)゚。 </font>
> </p>
>
> <p>
> 果たして、このツールは泥沼スパゲッティコードを断ち切る勇者となるか、使えない愚者になるか。まだまだ評価が必要そうだ。
> </p>
>
> <p>
> これさえあれば、どんなに依存関係があって、Undef関数だらけなコンパイルエラーを攻略できそうだ。たとえそれが、10000行くらいのソースコードであっても・・・orz??
> </p>
>
> <p>
> ああ、無情。でもガンバルじゃん。
> </p>
>
> <h3>
> おまけ 書籍の紹介
> </h3>
>
> <h4>
> Embedded Testing with Unity and CMock
> </h4>
>
> <p>
> UnityとCMockの使い方について、電子書籍とペーパーブックが出ている。6ドルくらい。
> </p>
>
> <p>
> <a href="https://www.lulu.com/shop/mark-vandervoord/embedded-testing-with-unity-and-cmock/ebook/product-17422227.html" target="_blank"><img class="alignleft" border="0" alt="" align="left" src="https://capture.heartrails.com/150x130/shadow?https://www.lulu.com/shop/mark-vandervoord/embedded-testing-with-unity-and-cmock/ebook/product-17422227.html" width="150" height="130" /></a> <a style="color: #0070c5" href="https://www.lulu.com/shop/mark-vandervoord/embedded-testing-with-unity-and-cmock/ebook/product-17422227.html" target="_blank">Embedded Testing with Unity and CMock by Mark VanderVoord (eBook) - Lulu</a> <img border="0" alt="" src="https://b.hatena.ne.jp/entry/image/https://www.lulu.com/shop/mark-vandervoord/embedded-testing-with-unity-and-cmock/ebook/product-17422227.html" />
> </p>
>
> <p>
> <br style="clear: both" />
> </p>
>
> <p>
> 内容はとても異色だ。まず、ページが正方形なのが、なんかおかしい。物語形式で話が進んでいく。途中に可愛い?!マンガチックなイラストがたくさんでてくる。文学表現が初めの方はおおくて、知らない単語が多かったりした。前半がUnityの話、後半がCMockの話。
> </p>
>
> <p>
> <a href="https://futurismo.biz/archives/1281">UnityとCMockの使い方が分かる本『Embedded Testing with Unity and CMock』を読んだ | Futurismo</a>
> </p>
>
> <h4>
> test driven development for embedded c
> </h4>
>
> <p>
> UnityおよびCMockについては、以下の書籍でも話題にでている。
> </p>
>
> <div style="text-align: left; padding-bottom: 20px; zoom: 1; font-size: small; overflow: hidden" class="amazlink-box">
> <div style="clear: both" class="amazlink-list">
> <div style="margin: 0px 12px 1px 0px; float: left" class="amazlink-image">
> <a href="https://www.amazon.co.jp/Test-Driven-Development-Embedded-Pragmatic-Programmers/dp/193435662X%3FSubscriptionId%3DAKIAJBCXQ4WQGJ7WU3WA%26tag%3Dsleephacker-22%26linkCode%3Dxm2%26camp%3D2025%26creative%3D165953%26creativeASIN%3D193435662X" rel="nofollow" target="_blank"><img style="border-bottom: medium none; border-left: medium none; border-top: medium none; border-right: medium none" src="https://ecx.images-amazon.com/images/I/51AWF3--mpL._SL160_.jpg" /></a>
> </div>
>
> <div style="margin-bottom: 10px; height: 160px" class="amazlink-info">
> <div style="line-height: 120%; margin-bottom: 10px" class="amazlink-name">
> <a href="https://www.amazon.co.jp/Test-Driven-Development-Embedded-Pragmatic-Programmers/dp/193435662X%3FSubscriptionId%3DAKIAJBCXQ4WQGJ7WU3WA%26tag%3Dsleephacker-22%26linkCode%3Dxm2%26camp%3D2025%26creative%3D165953%26creativeASIN%3D193435662X" rel="nofollow" target="_blank">Test-Driven Development for Embedded C (Pragmatic Programmers)</a>
> </div>
>
> <div style="line-height: 120%; margin-top: 5px; font-size: 80%" class="amazlink-powered">
> posted with <a title="アマゾンアフィリエイトリンク作成ツール" href="https://amazlink.keizoku.com/" target="_blank">amazlink</a> at 13.04.21
> </div>
>
> <div class="amazlink-detail">
> James W. Grenning
> </div>
>
> <div style="float: left" class="amazlink-sub-info">
> <div style="margin-top: 5px" class="amazlink-link">
> <img src="https://amazlink.fuyu.gs/icon_amazon.png" width="18" /><a href="https://www.amazon.co.jp/Test-Driven-Development-Embedded-Pragmatic-Programmers/dp/193435662X%3FSubscriptionId%3DAKIAJBCXQ4WQGJ7WU3WA%26tag%3Dsleephacker-22%26linkCode%3Dxm2%26camp%3D2025%26creative%3D165953%26creativeASIN%3D193435662X" rel="nofollow" target="_blank">Amazon</a>
> </div></p>
> </div></p>
> </div></p>
> </div>
> </div>
>
> <p>
> (電子書籍はここから) <a href="https://pragprog.com/book/jgade/test-driven-development-for-embedded-c">https://pragprog.com/book/jgade/test-driven-development-for-embedded-c</a>
> </p>
>
> <p>
> Unityはけっこうベージが割かれているけれども、CMockについては2ページくらい。どちらかというと、Mockを自力で作成するためのアイデアに役に立つ。
> </p>
>
> <p>
> <a href="https://futurismo.biz/archives/172">そろそろ『test driven development for embedded c』について書いてみる | Futurismo</a>
> </p>