web-dev-qa-db-ja.com

組み込み開発の単体テスト時のベストプラクティス

組み込みシステム用に記述されたユニットテストコードのベストプラクティス戦略を探しています。組み込みシステムとは、デバイスドライバー、ISRハンドラーなど、かなり金属に近いものなどのコードを意味します。

ユニットテストのほとんどは、ICEを使用してハードウェア上でテストしないと実行できません。組み込みユニットは、機械式スイッチ、ステッピングモーター、電球などの他の刺激に接続する必要がある場合もあります。これは通常、手動で行われます。自動化は優れていますが、達成するのは難しく、費用がかかります。

更新

組み込みプロジェクトのテストでかなり成功しているように見えるCテストフレームワークに出くわしました。ハードウェアをモックするアイデアを使用しています。 nityCMock 、そしておそらく Ceedling をチェックしてください。

更新06Jul2016

来た cmocka -より積極的に取り組んでいるようだ。

46
tehnyit

できるだけ早い段階でハードウェアの依存関係から離れて抽象化し、ソフトウェアエミュレーション/テストハーネスでシステムを構築して、あらゆる種類のテストフレームワークを有効にします。多くの場合、私の開発用PCは、システム全体の95%以上をテストするために使用されました。追加のオーバーヘッド(抽象化の別のレイヤー)のコストは、その抽象化の結果として生成されたよりクリーンなコードによって簡単に取り消されました。

組み込みシステムの真のベアメタルパーツのテストは、通常、個別のアプリケーション(ユニットテスト?)であり、アプリケーションが達成することさえ期待できる以上にファームウェアを攻撃します。自動化は実行できますが、コストはかかりますが、一般的ではありません。

それ以外の場合を除き、完全なICEを含む単体テストハードウェアハーネスを構築する予算があります。一般に機能テストは小さいので、これは絶対に問題ありません。

28
mattnz

開発に必要なツールはシグナルインジェクターです。組み込みシステムには、ホストシステムと(通常はデバッグ用に予約されたシリアルポートを介して)インターフェースする方法があります。これを使用してテストデータを送信します(最適なオプションは簡潔なASCII形式なので、人間でも簡単にシミュレーションできます)。

私はあなたの質問のこの部分に完全に同意しません:「自動化は素晴らしいですが、達成するのは難しく、費用がかかります。」

TeraTermをシリアルポート信号インジェクターとして使用し、いくつかのTeraTermマクロを記述して(約20分かかります)、ドライバー層、O/Sなど、組み込みシステムの任意の部分に対して実行できる自動テストの巨大なスイートがあります。レイヤー4-5などTeraTerm: http://en.sourceforge.jp/projects/ttssh2/

組み込みシステムでシリアルポートが利用できない場合は、ハードウェアツールを使用してUSB /シリアルポートデータをデジタル信号に変換します(これも安価で簡単に成し遂げる)。これを読んでいるとき、私は30ドルのマイクロコントローラーボード(UBW: http://www.schmalzhaus.com/UBW32/ )を使用して、TeraTermマクロを介して刺激を注入することにより、組み込みシステムを本番環境でテストします。 USB /シリアル経由でマイクロコントローラーに送信されます。マイクロコントローラーは、変更されたファームウェアを実行して、ターゲットの組み込みシステムのデジタル入力を実行し、デジタル出力を監視します。これに関連して、pythonスクリプト(pyserialとpexpectを使用)を開発して、データインジェクションとデータ検証を自動化しました。どれもhardそしてそれはどれも高価ではありません私の経験では、テストチームが経験がなくこれらを理解できない場合、マネージャは多額の費用(30,000ドルのテスト機器など)を費やしています。簡単な解決策-残念ながら、汎用のビッグアイアン機器には、ターゲットシステムの最悪の場合のタイミングなどをキャッチするテストケースが含まれていないことがよくあります。このため、安価な方法が推奨されます。テスト範囲。信じられないかもしれません。

15

これは非常に難しい問題です。

私は実際に組み込みシステム用のユニットテストハーネスを設計しました。これにより、ハードウェアイベント/割り込みのシミュレーションが可能になり、実行のタイミングを制御できます(同時実行によるすべての可能なインターリーブを確実にカバーするため)。 2年以上プログラマーが実際にそれを実装してそれを動作させるために。そのプロジェクトはプロプライエタリな開発ですが、同様の(設計がより単純な)プロジェクトが利用可能です here

つまり、自動化は素晴らしいことです。はい、それを達成するのは非常に困難で費用がかかります。はい、時々それをしなければなりません。まれに、私の経験では、ほとんどの場合、ステッピングモーターと電球を使用してすべて手動で動作させる方が速くて安価です。

5
littleadv

編集:私の答えはmattnzのものに近いと思います...


私はこの問題を他のコードと関連付けたいと思います。すべてのテストは、コードの外部に依存するものです(システムクロック、永続的なファイルシステム、データベース、外部のWebサービスへの接続など)。私はそれらすべてに同じポリシーを提案します。コードの2つのレイヤーで2つのレベルを分離するです。

単一の外部操作のテスト

各操作を物理的にテストしたい場合があります。システムクロックが正しい時間を提供していることを確認し、ファイルが実際に書き込まれた内容を記憶していることを確認し、デバイスが単一の操作を受信することを確認します...

これらのテスト:

  • できるだけ単純にする必要があります:アルゴリズムはまったくなく、条件もループもありません
  • 順序に依存し、マシンに依存する可能性があります。厳密な順序に従って、各ハードウェアで繰り返す必要があります
  • プロジェクト全体を通じてほとんど安定しているため、頻繁に実行する必要はありません
  • 手動で実行することはオプションです。過度に複雑ではないにしても、自動化はさらに優れています
  • 注意:テストされているものはコードではありません、これはコードに必要なツールです...したがって、これをテストすることはオプションである可能性があり、別のチームによって行われた可能性があります...

外部操作をフックするロジック(コード、アルゴリズム)のテスト

実際の外部操作を行うためのコードのレイヤーを用意し、簡単に模擬できるインターフェースとしてそれらを非表示にすることで、ロジックは実際の物理デバイスに依存しなくなります...

通常のプロジェクトと同様に、簡単にテストできますもうテストが難しい埋め込みコードにはもういません

4
KLE

埋め込まれていないTDDと同様に、 mock objects は間違いなくあなたの友達です。

基盤となるハードウェアへのインターフェースをクリーンでシンプルに保つことで、最低レベルより上のすべてをモックアウトできるようになり、より簡単にそれを実行できるようになります。 。

また、プロジェクトのかなり後期になるまでオンラインでテストできない可能性があるからといって、オンラインテストのスイートも用意するべきではないという意味ではありません。

これらは(最初は)オフラインでテストできなかったビットのみをテストする必要があります。もちろん、それはTDDではありません(事前にテストを作成しているため)が、オフラインTDD開発では、ハードウェアインターフェイスがどのように見える必要があるか、したがってどのオンラインテストを実行する必要があるかについての良いアイデアが得られるはずです。

また、オンライン開発のコストがオフライン開発よりもはるかに高い場合(私が作業する場所と同じように)、十分に理解された一連のテストを実行するためにオンラインで多くの時間を節約できます。

3
Mark Booth

組み込みCPUシミュレータは、通常、ハードウェアもシミュレートするようにプログラムできます。 Xen以外のすべての仮想化テクノロジーがそれを行います。しかし、いくつかの物理アドレスまたはx86ではI/Oバス上のアドレスにいくつかのレジスタを装うコードを書く必要があり、次に、ソフトウェアが物理アドレスであるかのように、これらのアドレスへの読み取りと書き込みに応答する必要があります。制御およびステータスレジスタがアクセスされていたチップ。

これを行う場合は、QEMUを変更することをお勧めします。しかし、それは簡単ではありません。この種のことは通常、I/O用にマイクロコントローラーと他のコアを備えたカスタムチップを設計する場合にのみ行われます。

ARM Holdingsが販売している開発システムはこれを提供しており、QEMUをハックするよりも扱いが簡単ですが、非常に高価です。

単一のサブルーチンを実行するいくつかのオープンソースARMエミュレーターがあり、それ自体が他のサブルーチンを呼び出すことができ、ハードウェアアクセスに依存しないサブルーチンのパフォーマンスのチューニングをデバッグするために使用できます。これらの1つを使用して、AES暗号化装置をARM7TDMI向けに最適化しました。

CまたはC++で単純な単体テストハーネスを記述し、テスト対象のクラスまたはサブルーチンをそれにリンクして、シミュレーターで実行できます。

LinuxやMac OS Xのカーネルコードを単体テストする方法について、私は何年にもわたって同様の問題について考えてきました。それは可能であるべきですが、私は実際に試したことはありません。 1つは、単体テストフレームワークをカーネルに直接リンクして、コードを個別にテストするのではなく、完全なカーネルを構築することです。次に、なんらかの外部インターフェイスからユニットテストを開始します。

コードカバレッジツールを使用してから、外部インターフェイスを介してファームウェアを完全なパッケージとしてテストすると、生産性が向上する可能性があります。カバレッジツールはまだテストされていないコードパスを見つけるため、カバレッジを増やすために外部テストを追加できます。

3
Mike Crawford

組み込み開発では、アプリケーション全体(ハードウェアを含む)が動作することを確認するために 境界スキャン を行うことがよくあります。システムのデバッグについては [〜#〜] jtag [〜#〜] も参照してください。

ハードウェアにリンクせずに純粋なソフトウェアルーチンをテストするには、 Check のような標準のCユニットテストフレームワークを使用します。ただし、メモリの制限に注意してください(特に小さなデバイスのスタックスペースなど)。契約を知ってください!ハードウェアからソフトウェアルーチンを抽象化して、より大きなテストカバレッジを取得することもできますが、これは通常、小さなPICやAVRなどの組み込みデバイスのパフォーマンスの点でコストがかかります。ただし、ハードウェアポートをモックしてより広いカバレッジを実現できます(もちろん、そのモックをテストすることもできます)。

チップまたは回路シミュレータ用のエミュレータを使用することもできますが、これらの種類のツールは高価で(特に組み合わせて)、複雑です。

1
Falcon