I need to add unit tests to the class (SensorClient) that contains a pointer to the object of the class (VelocitySensor) marked as final. And what's important is that I can't modify the VelocitySensor class. Therefore, I can't change VelocitySensor methods to virtual... Also, I would like to avoid any "friend" classes or tricks with #define.
VelocitySensor class:
//sensor_service.hpp
#pragma once
#include <string>
namespace sensor
{
class VelocitySensor final
{
public:
VelocitySensor() : velocity(20), sensorName("VelocitySensor") {}
double getVelocity() const
{
return velocity;
}
std::string getSensorName() const
{
return sensorName;
}
void calculateSomething()
{
const double time = 10;
auto vel = getVelocity();
auto distance = vel * time;
//...
}
private:
std::string sensorName;
double velocity;
};
}
SensorClient class:
//sensor_client.hpp
#pragma once
#include "sensor_service.hpp"
#include <memory>
#include <iostream>
namespace sensor
{
class SensorClient
{
public:
SensorClient() : sensor(std::make_unique<VelocitySensor>()) {}
double getVelocity() const
{
return sensor->getVelocity();
}
void calculateNumOfTyres()
{
sensor->calculateSomething();
}
std::string getVehicleName() const
{
return sensor->getSensorName();
}
private:
std::unique_ptr<VelocitySensor> sensor;
};
};
I found three ways to solve that issue. Maybe there is another better solution (I hope so :))
- The first one is to templetize SensorCLient, and as a result, it should be quite easy to mock VelocitySensor during tests.
#pragma once
#include "sensor_service.hpp"
#include <memory>
#include <iostream>
namespace sensor
{
template <typename T>
class SensorClientTemplate
{
public:
SensorClientTemplate(std::unique_ptr<T> _sensor = std::make_unique<VelocitySensor>())
: sensor(std::move(_sensor)) {}
virtual ~SensorClientTemplate() = default;
double getVelocity() const
{
return sensor->getVelocity();
}
void calculateSomething()
{
sensor->calculateSomething();
}
std::string getSensorName() const
{
return sensor->getSensorName();
}
private:
std::unique_ptr<T> sensor;
};
using SensorClient = SensorClientTemplate<SensorService>;
};
For me it looks quite good. But maybe there is another solution which doesn't require the use of templates.
- The second one is to use the Adapter Pattern.
#pragma once
#include "sensor_service.hpp"
#include <memory>
#include <iostream>
namespace sensor
{
class ISensor
{
public:
virtual ~ISensor() = default;
virtual double getVelocity() const = 0;
virtual std::string getSensorName() const = 0;
virtual void calculateSomething() = 0;
};
class SensorAdapter : public ISensor
{
public:
void calculateSomething() override
{
sensor.calculateSomething();
}
double getVelocity() const override
{
return sensor.getVelocity();
}
std::string getSensorName() const override
{
return sensor.getSensorName();
}
private:
VelocitySensor sensor;
};
class SensorClient
{
public:
SensorClient(std::unique_ptr<ISensor> _sensor = std::make_unique<SensorAdapter>())
: sensor(std::move(_sensor)) {}
virtual ~SensorClient() = default;
double getVelocity() const
{
return sensor->getVelocity();
}
void calculateSomething()
{
sensor->calculateSomething();
}
std::string getSensorName() const
{
return sensor->getSensorName();
}
private:
std::unique_ptr<ISensor> sensor;
};
};
Now I have an issue with adding tests to the adapter class... I moved the issue with mocking VelocitySensor to a new class. I would like to take care of code coverage... So this is not a solution for me.
- Pimpl
I think I can use the Pimpl idom to hide implementation details (including a pointer to the VelocityObject) in the cpp file, and as a result, I should be able to mock the implementation (Now I don't need to mock VelocitySensor). But in that case, the implementation won't be covered by unit tests. :( I found something about using factories to solve that issue, but I'm not sure how I can use that in my case,
Aucun commentaire:
Enregistrer un commentaire