ROS Resources: Documentation | Support | Discussion Forum | Index | Service Status | ros @ Robotics Stack Exchange
Ask Your Question
0

Interfaces of ROS classes for mocking and dependency injection

asked 2020-11-25 21:43:08 -0600

Rufus gravatar image

updated 2020-11-25 21:57:10 -0600

I am trying to write unit tests for my class that interacts with various ROS ServiceClient and SimpleActionClient. Since I'm fairly confident in ROS and don't want to handle the indeterminacy of network latency in my unit tests, I plan to have my unit tests cover the interface between my class and ROS only.

Hence, I would need to mock the ServiceClient and SimpleActionClient. In order to mock, the things I am mocking needs to implement an interface (lest I be forced to use this uglier method.

My question is, does the ServiceClient and SimpleActionClient implement any interface that I can mock? If not, is there any best practices for mocking common ROS classes?

My current implementation is to use dependency injection to allow me to separate and swap out the concrete ROS stuff with (hopefully) mocked ROS stuff

PayloadController::PayloadController(
        ros::ServiceClient& service_client,
        actionlib::SimpleActionClient<...>& action_client)

PS:: To my blessing, tf2_ros::Buffer implements the BufferInterface which allowed me to mock and unit test code that used TF2 easily

edit retag flag offensive close merge delete

1 Answer

Sort by ยป oldest newest most voted
0

answered 2021-01-18 20:55:10 -0600

Rufus gravatar image

updated 2021-01-18 21:12:46 -0600

I resorted to writing my own wrapper to achieve my needs. Below is an example of wrapping a SimpleActionClient

#pragma once

#include <actionlib/client/simple_action_client.h>
#include "actionlib/client/action_client.h"
#include "actionlib/client/simple_goal_state.h"
#include "actionlib/client/simple_client_goal_state.h"
#include "actionlib/client/terminal_state.h"

template <class ActionSpec>
class SimpleActionClientInterface
{
    protected:
        ACTION_DEFINITION(ActionSpec);
    public:
        virtual void sendGoal(const Goal& goal) = 0;
        virtual void waitForResult() = 0;
        virtual actionlib::SimpleClientGoalState getState() = 0;
};

template <class ActionSpec>
class SimpleActionClientWrapper : public SimpleActionClientInterface<ActionSpec>
{
    protected:
        ACTION_DEFINITION(ActionSpec);

    public:
        SimpleActionClientWrapper(actionlib::SimpleActionClient<ActionSpec>& client) :
            client(client)
        {};

        virtual void sendGoal(const Goal& goal)
        {
            client.sendGoal(goal);
        };

        virtual void waitForResult()
        {
            client.waitForResult();
        };

        virtual actionlib::SimpleClientGoalState getState()
        {
            return client.getState();
        };

    private:
        actionlib::SimpleActionClient<ActionSpec>& client;
};
edit flag offensive delete link more

Question Tools

2 followers

Stats

Asked: 2020-11-25 21:43:08 -0600

Seen: 342 times

Last updated: Jan 18 '21