はじめに

仕事では、今までは新規開発だった。なので、真っ白なテキストからテストを書くことができた。しかし、これからは、流用開発。既存コードに機能追加しなければならない。そしてその既存コードはうんざりするほどの量があり、かつテストは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>