조건 노드의 단위 테스트 (Condition Node Unit Testing)
1. 개요
조건 노드의 단위 테스트는 개별 조건 노드가 다양한 입력에 대해 올바른 반환 상태(SUCCESS 또는 FAILURE)를 생성하는지를 검증하는 테스트이다. 조건 노드는 행동 트리의 의사 결정을 좌우하는 핵심 요소이므로, 정상 조건, 경계 조건, 이상 조건 등 다양한 시나리오에 대한 체계적인 테스트가 필수적이다.
2. 테스트 프레임워크
2.1 Google Test와 BehaviorTree.CPP
BehaviorTree.CPP 기반 조건 노드의 단위 테스트에는 Google Test(gtest) 프레임워크가 일반적으로 사용된다.
#include <gtest/gtest.h>
#include <behaviortree_cpp/bt_factory.h>
#include <rclcpp/rclcpp.h>
2.2 테스트 환경 설정
조건 노드의 테스트를 위해 모의 블랙보드, 모의 ROS2 노드, 모의 토픽 발행자 등의 테스트 환경을 구성한다.
class ConditionNodeTest : public ::testing::Test
{
protected:
void SetUp() override
{
rclcpp::init(0, nullptr);
node_ = std::make_shared<rclcpp::Node>("test_node");
factory_ = std::make_shared<BT::BehaviorTreeFactory>();
// 조건 노드 등록
BT::RosNodeParams params;
params.nh = node_;
factory_->registerNodeType<IsValueAboveThreshold>(
"IsValueAboveThreshold", params);
}
void TearDown() override
{
rclcpp::shutdown();
}
rclcpp::Node::SharedPtr node_;
std::shared_ptr<BT::BehaviorTreeFactory> factory_;
};
3. 테스트 유형
3.1 정상 조건 테스트
기대되는 입력에 대해 올바른 결과를 반환하는지를 검증한다.
| 테스트 항목 | 입력 | 기대 결과 |
|---|---|---|
| SUCCESS 반환 | 조건 충족 값 | SUCCESS |
| FAILURE 반환 | 조건 미충족 값 | FAILURE |
| 경계값 테스트 | 임계값 근처 값 | 정확한 판정 |
3.2 이상 조건 테스트
비정상적 입력에 대한 안전한 동작을 검증한다.
| 테스트 항목 | 입력 | 기대 결과 |
|---|---|---|
| 메시지 미수신 | nullptr | FAILURE |
| NaN 값 | NaN 포함 메시지 | FAILURE |
| 무한대 값 | Inf 포함 메시지 | FAILURE |
| 빈 배열 | 빈 ranges[] | FAILURE |
4. 구현
4.1 SUCCESS/FAILURE 기본 테스트
TEST_F(ConditionNodeTest, ReturnsSuccessWhenAboveThreshold)
{
std::string xml = R"(
<root BTCPP_format="4">
<BehaviorTree ID="Test">
<Condition ID="IsValueAboveThreshold"
topic_name="/test_value"
threshold="5.0"/>
</BehaviorTree>
</root>
)";
auto tree = factory_->createTreeFromText(xml);
// 임계값 이상의 메시지 발행
auto pub = node_->create_publisher<std_msgs::msg::Float64>(
"/test_value", 10);
std_msgs::msg::Float64 msg;
msg.data = 7.0;
pub->publish(msg);
// 콜백 처리를 위한 스핀
rclcpp::spin_some(node_);
auto status = tree.tickOnce();
EXPECT_EQ(status, BT::NodeStatus::SUCCESS);
}
TEST_F(ConditionNodeTest, ReturnsFailureWhenBelowThreshold)
{
// ... (유사 구조, msg.data = 3.0으로 설정)
auto status = tree.tickOnce();
EXPECT_EQ(status, BT::NodeStatus::FAILURE);
}
4.2 경계값 테스트
TEST_F(ConditionNodeTest, BoundaryValueExactlyAtThreshold)
{
// threshold = 5.0, value = 5.0
// 설계에 따라 SUCCESS 또는 FAILURE
msg.data = 5.0;
pub->publish(msg);
rclcpp::spin_some(node_);
auto status = tree.tickOnce();
// 구현이 > (초과)인 경우 FAILURE, >= (이상)인 경우 SUCCESS
EXPECT_EQ(status, BT::NodeStatus::FAILURE);
}
TEST_F(ConditionNodeTest, BoundaryValueJustAboveThreshold)
{
msg.data = 5.001;
pub->publish(msg);
rclcpp::spin_some(node_);
auto status = tree.tickOnce();
EXPECT_EQ(status, BT::NodeStatus::SUCCESS);
}
4.3 메시지 미수신 테스트
TEST_F(ConditionNodeTest, ReturnsFailureWhenNoMessage)
{
// 메시지를 발행하지 않고 tick
auto status = tree.tickOnce();
EXPECT_EQ(status, BT::NodeStatus::FAILURE);
}
4.4 Running 미반환 검증
조건 노드는 절대로 RUNNING을 반환하지 않아야 한다.
TEST_F(ConditionNodeTest, NeverReturnsRunning)
{
for (int i = 0; i < 100; ++i)
{
auto status = tree.tickOnce();
EXPECT_NE(status, BT::NodeStatus::RUNNING);
}
}
5. 모의 객체를 활용한 테스트
5.1 모의 블랙보드
블랙보드 입력 포트의 값을 직접 설정하여 조건을 테스트한다.
TEST_F(ConditionNodeTest, BlackboardInputTest)
{
auto blackboard = BT::Blackboard::create();
blackboard->set("threshold", 10.0);
auto tree = factory_->createTreeFromText(xml, blackboard);
// ...
}
5.2 모의 ROS2 토픽
테스트 내부에서 직접 토픽을 발행하여 조건 노드에 데이터를 제공한다.
void publishTestMessage(double value)
{
auto pub = node_->create_publisher<std_msgs::msg::Float64>(
"/test_topic", 10);
std_msgs::msg::Float64 msg;
msg.data = value;
pub->publish(msg);
// 메시지가 구독자에게 전달되도록 스핀
auto start = std::chrono::steady_clock::now();
while (std::chrono::steady_clock::now() - start <
std::chrono::milliseconds(100))
{
rclcpp::spin_some(node_);
}
}
6. 테스트 조직화
6.1 테스트 분류
조건 노드의 단위 테스트는 다음과 같이 분류하여 체계적으로 관리한다.
| 분류 | 테스트 내용 |
|---|---|
| 정상 경로 | SUCCESS/FAILURE 기본 반환 |
| 경계값 | 임계값 근처의 정확한 판정 |
| 이상 입력 | NaN, Inf, nullptr, 빈 데이터 |
| 시간 관련 | 타임아웃, 메시지 신선도 |
| 블랙보드 | 포트 값 전달, 기본값 |
| 스레드 안전 | 동시 접근, 경쟁 조건 |
7. 설계 시 고려 사항
7.1 테스트의 독립성
각 테스트는 다른 테스트와 독립적으로 실행되어야 한다. ROS2 노드, 토픽, 블랙보드 등의 상태가 테스트 간에 영향을 미치지 않도록 SetUp()과 TearDown()에서 초기화와 정리를 수행한다.
7.2 비결정적 동작의 테스트
ROS2의 DDS 통신은 비결정적 지연을 포함하므로, 메시지 전달이 보장되는 시점을 정확히 예측할 수 없다. 테스트에서는 충분한 대기 시간을 설정하거나, 메시지 수신을 확인하는 동기화 메커니즘을 사용한다.
7.3 커버리지 목표
안전 관련 조건 노드에 대해서는 높은 코드 커버리지(예: 분기 커버리지 90% 이상)를 목표로 설정하여, 테스트되지 않은 코드 경로를 최소화하여야 한다.
8. 참고 문헌
- Colledanchise, M., & Ogren, P. (2018). Behavior Trees in Robotics and AI: An Introduction. CRC Press.
- BehaviorTree.CPP 공식 문서. https://www.behaviortree.dev/
- Google Test 문서. https://google.github.io/googletest/
| 버전 | 날짜 | 변경 사항 |
|---|---|---|
| v0.1 | 2026-04-04 | 초안 작성 |