はじめに
ROSコードのユニットテストの枠組みとして,rostest
と言うものがあります.
詳細はROS Wiki - rostestあたりを覗くとなんとなく分かった気になれます.
このrostest
の実行方法が2つあることに疑問を感じ,調べたことを共有することとします.
rostest
の2つの実行方法とは?
下記2つのコマンドを指します.
catkin_make run_tests
catkin_make test
出力されるログが異なるのですが,どっちでもテストはできちゃいます.
ドキュメントにはなんて書いてある?
ROS Wiki - rostestを見てみると,catkin
のタブを選択するとrun tests
の説明が出てきて,rosbuild
のタブを選択するとtest
の説明が出てきます.
「ビルドシステムでコマンドが違うだけ,ちゃんちゃん」という話なら良かったのですが,いかんせんcatkin
でもcatkin_make test
が出来てしまいます.エイリアスとして残してくれているとかそういう位置づけなのかもしれませんが,じゃあ何が違うのかが余計気になってきます.
と思って調べてみても,これら2つのコマンドの違いをまとめた記述が全然出てこない笑.
結局,利用者側から見たrun_tests
とtest
の違いは?
色々試したところ,各々下記のような特長があることが分かりました.
catkin_make runtests
Pros
- cppのテスト(
gtest
)を使う場合,テストコードを初回ビルドするのにこちらが必須. - テストノードが非同期でどんどん実行されるのでテスト実行時間自体は短い.
- 詳細なエラーログを吐く上,テスト別の実行もできるので,デバッグ時はこちら便利.
-
run_tests_
でTabを押すと補完候補が出るので,特定のテストノードの実行が可能.
-
Cons
- テストがたくさんある場合,どのテストが
Fail
したのかがパッと見てわかりづらい. - 常に0(success)を返すので,これ
run_tests
単体ではテストがFail
したかを判別できない.- 判定には
catkin_test_results
というコマンドを使う.直前のテストで1つでもFail
があれば0ではない値を返す.
- 判定には
catkin_make test
Pros
- テスト全体の視認性が非常に良い.
- 一度ビルドしたあとなら,普段使いのチェック用にするのが良さげ.
- こちらでエラーを吐いたテストは,
catkin_make run_tests
側で詳細にデバッグ. - 直前のテストで1つでも
Fail
があれば0ではない値を返すので,test
単体でテストと判定が可能.
Cons
- cppのテスト(
gtest
)を使う場合,こちらではビルドしない.- 必ず
catkin_make runtests
のあとに,catkin_make test
を行う必要がある.
- 必ず
-
Fail
時にログを出してくれない.ただFail
したとしか言ってくれない. - シーケンシャルに実行するのでテスト実行時間が長い.
どう使い分けるか?
というわけで,私は勝手に下記のように使い分けることにしています.
- 初回はビルドのため必ず
catkin_make run_tests
を実行する.- でもどれが
Fail
したのか良くわからない.困った.
- でもどれが
- そこで,ビルドが終わったのを確認して別のターミナルで
catkin_make test
でFail
するテストを確認する.- でも,
Fail
の理由は分からない.困った.
- でも,
- そこで,
Fail
したテストを確認したら,catkin_make run_tests_hogehoge
で,特定のテストだけ実行して詳細なデバッグログを確認する.
これにて一応一見落着.何か,行ったり来たりで,本当にこれで正しいのかがよく分かりません ^^;
上記仕様を手軽に確かめる方法
参考リポジトリ
下記リポジトリがシンプルととても分かり易いです.
ディレクトリ構成
テスト部分だけ抜粋したディレクトリ構成です.
ros-travis-integration/
│
└ ros_pkg_with_tests/
│
├ scripts/
│ │
│ ├ test/
│ │ │
│ │ └ test_double.py
│ │ ↑ python用 テストコード.
│ └ double.py
│ ↑ python コード実体.
├ src/
│ │
│ ├ test/
│ │ │
│ │ └ test_square.cpp
│ │ ↑ cpp用 テストコード.
│ ├ square.cpp
│ │ ↕ cpp コード実体
│ └ square.h
│
├ package.xml
│
└ CMakeLists.txt
挙動の確認
今回はこのコードの中身は追わず,単にrun_tests
とtest
の挙動を確認することとします.
<your_ws>/src/
に,上記リポジトリをクローンしておきます.
$ cd <your_ws>
$ catkin_make
$ source devel/setup.bash
テストコードビルド前にcatkin_make test
を試す
一回,test
を試してみます.ビルドされていないテストコードは実行できないはずです.
$ cd <your_ws>
$ catkin_make test
Base path: /<your_ws>
Source space: /<your_ws>/src
Build space: /<your_ws>/build
Devel space: /<your_ws>/devel
Install space: /<your_ws>/install
####
#### Running command: "make cmake_check_build_system" in "/<your_ws>/build"
####
####
#### Running command: "make test -j8 -l8" in "/<your_ws>/build"
####
Running tests...
Test project /<your_ws>/build
Start 1: _ctest_ros_pkg_with_tests_gtest_ros_pkg_with_tests-test
1/2 Test #1: _ctest_ros_pkg_with_tests_gtest_ros_pkg_with_tests-test ...***Failed 0.05 sec
Start 2: _ctest_ros_pkg_with_tests_nosetests_scripts.test
2/2 Test #2: _ctest_ros_pkg_with_tests_nosetests_scripts.test .......... Passed 0.47 sec
50% tests passed, 1 tests failed out of 2
Total Test time (real) = 0.53 sec
The following tests FAILED:
1 - _ctest_ros_pkg_with_tests_gtest_ros_pkg_with_tests-test (Failed)
Errors while running CTest
make: *** [test] エラー 8
catkin_make run_tests
を試す
$ cd <your_ws>
$ catkin_make run_tests
Base path: /<your_ws>
Source space: /<your_ws>/src
Build space: /<your_ws>/build
Devel space: /<your_ws>/devel
Install space: /<your_ws>/install
####
#### Running command: "make cmake_check_build_system" in "/<your_ws>/build"
####
####
#### Running command: "make run_tests -j8 -l8" in "/<your_ws>/build"
####
Scanning dependencies of target clean_test_results_ros_pkg_with_tests
Scanning dependencies of target gtest
[ 25%] Built target square
[ 50%] Removing test result files from '/<your_ws>/build/test_results/ros_pkg_with_tests'
- removing '/<your_ws>/build/test_results/ros_pkg_with_tests/MISSING-gtest-ros_pkg_with_tests-test.xml'
- removing '/<your_ws>/build/test_results/ros_pkg_with_tests/nosetests-scripts.test.xml'
Building CXX object gtest/CMakeFiles/gtest.dir/src/gtest-all.cc.o
[ 50%] Built target clean_test_results_ros_pkg_with_tests
Linking CXX shared library libgtest.so
[ 50%] Built target gtest
Scanning dependencies of target gtest_main
[ 75%] Building CXX object gtest/CMakeFiles/gtest_main.dir/src/gtest_main.cc.o
Linking CXX shared library libgtest_main.so
[ 75%] Built target gtest_main
Scanning dependencies of target ros_pkg_with_tests-test
[100%] Building CXX object ros-travis-integration/ros_pkg_with_tests/CMakeFiles/ros_pkg_with_tests-test.dir/src/test/test_square.cpp.o
Linking CXX executable /<your_ws>/devel/lib/ros_pkg_with_tests/ros_pkg_with_tests-test
[100%] Built target ros_pkg_with_tests-test
Scanning dependencies of target tests
[100%] Built target tests
Scanning dependencies of target _run_tests_ros_pkg_with_tests_nosetests_scripts.test
Scanning dependencies of target _run_tests_ros_pkg_with_tests_gtest_ros_pkg_with_tests-test
-- run_tests.py: execute commands
-- run_tests.py: execute commands
/usr/bin/cmake -E make_directory /<your_ws>/build/test_results/ros_pkg_with_tests
/<your_ws>/devel/lib/ros_pkg_with_tests/ros_pkg_with_tests-test --gtest_output=xml:/<your_ws>/build/test_results/ros_pkg_with_tests/gtest-ros_pkg_with_tests-test.xml
/usr/bin/nosetests-2.7 -P --process-timeout=60 --where=/<your_ws>/src/ros-travis-integration/ros_pkg_with_tests/scripts/test --with-xunit --xunit-file=/<your_ws>/build/test_results/ros_pkg_with_tests/nosetests-scripts.test.xml
[==========] Running 2 tests from 1 test case.
[----------] Global test environment set-up.
[----------] 2 tests from TestSuite
[ RUN ] TestSuite.squareTwo
[ OK ] TestSuite.squareTwo (0 ms)
[ RUN ] TestSuite.squareFour
[ OK ] TestSuite.squareFour (0 ms)
[----------] 2 tests from TestSuite (0 ms total)
[----------] Global test environment tear-down
[==========] 2 tests from 1 test case ran. (0 ms total)
[ PASSED ] 2 tests.
-- run_tests.py: verify result "/<your_ws>/build/test_results/ros_pkg_with_tests/gtest-ros_pkg_with_tests-test.xml"
[100%] Built target _run_tests_ros_pkg_with_tests_gtest_ros_pkg_with_tests-test
Scanning dependencies of target _run_tests_ros_pkg_with_tests_gtest
[100%] Built target _run_tests_ros_pkg_with_tests_gtest
....
----------------------------------------------------------------------
Ran 4 tests in 0.001s
OK
-- run_tests.py: verify result "/<your_ws>/build/test_results/ros_pkg_with_tests/nosetests-scripts.test.xml"
[100%] Built target _run_tests_ros_pkg_with_tests_nosetests_scripts.test
Scanning dependencies of target _run_tests_ros_pkg_with_tests_nosetests
[100%] Built target _run_tests_ros_pkg_with_tests_nosetests
Scanning dependencies of target _run_tests_ros_pkg_with_tests
[100%] Built target _run_tests_ros_pkg_with_tests
Scanning dependencies of target run_tests
[100%] Built target run_tests
ビルドがされています.
ログが詳細なのは良いのですが,少しごちゃごちゃしています ^^;
このサンプルではテストの数が少ないのでまだいいほうですが,テスト数がウン10個とかになると,もはやパット見ただけではわけが分からなくなります笑.こっちとしては,まずは単純にテストが通ったかどうかだけを知りたいのです.
またcatkin_make test
を試す
というわけで,晴れてビルドされたコードでこちらを試してみます.
$ cd <your_ws>
$ catkin_make test
Base path: /<your_ws>
Source space: /<your_ws>/src
Build space: /<your_ws>/build
Devel space: /<your_ws>/devel
Install space: /<your_ws>/install
####
#### Running command: "make cmake_check_build_system" in "/<your_ws>/build"
####
####
#### Running command: "make test -j8 -l8" in "/<your_ws>/build"
####
Running tests...
Test project /<your_ws>/build
Start 1: _ctest_ros_pkg_with_tests_gtest_ros_pkg_with_tests-test
1/2 Test #1: _ctest_ros_pkg_with_tests_gtest_ros_pkg_with_tests-test ... Passed 0.04 sec
Start 2: _ctest_ros_pkg_with_tests_nosetests_scripts.test
2/2 Test #2: _ctest_ros_pkg_with_tests_nosetests_scripts.test .......... Passed 0.34 sec
100% tests passed, 0 tests failed out of 2
Total Test time (real) = 0.39 sec
なんと分かりやすいこと!
catkin_make run_tests_ros_pkg_with_tests_nosetests_scripts.test
を試す
仮に_ctest_ros_pkg_with_tests_nosetests_scripts.test
がFail
したとして,このテストだけを実行してみます.
$ cd <your_ws>
$ catkin_make run_tests_ros_pkg_with_tests_nosetests_scripts.test
Base path: /<your_ws>
Source space: /<your_ws>/src
Build space: /<your_ws>/build
Devel space: /<your_ws>/devel
Install space: /<your_ws>/install
####
#### Running command: "make cmake_check_build_system" in "/<your_ws>/build"
####
####
#### Running command: "make run_tests -j8 -l8" in "/<your_ws>/build"
####
Scanning dependencies of target run_tests_ros_pkg_with_tests_nosetests_scripts.test
-- run_tests.py: execute commands
/usr/bin/cmake -E make_directory /<your_ws>/build/test_results/ros_pkg_with_tests
/usr/bin/nosetests-2.7 -P --process-timeout=60 --where=/<your_ws>/src/ros-travis-integration/ros_pkg_with_tests/scripts/test --with-xunit --xunit-file=/<your_ws>/build/test_results/ros_pkg_with_tests/nosetests-scripts.test.xml
....
----------------------------------------------------------------------
Ran 4 tests in 0.001s
OK
-- run_tests.py: verify result "/<your_ws>/build/test_results/ros_pkg_with_tests/nosetests-scripts.test.xml"
Built target run_tests_ros_pkg_with_tests_nosetests_scripts.test
単一のテストコードのログだけなので,先ほどcatkin_make run_tests
したときよりはスッキリです.
なんとなく,提案した使い分けルールの雰囲気を感じてもらえたなら,幸いです.
おわりに
run_tests
とtest
の違いについてまとめてみました.
使い分けについては,一般的な方法かどうかは分からないので,悪しからず… ^^;
(run_tests
側のオプションでうまいことならないのかと感じているのですが,それっぽいオプションが見当たりませんでした….)