Reputation: 2190
I am attempting to write a mock for a class with a private vector that will insert data into the private vector. However, I am not seeing a way to do it with Google Mock. Ideally, I would like to not have anything related to testing in my interface. Also, I would not like to make the private vector protected and subclass the class and add an accessor method, as this would cause my code to leak its implementation.
Here's what I have so far. What I'm trying to accomplish is to insert data with the Fake class, and use the Mock class to Invoke Real::first() on the pointer to the class of Fake (so that I can use Fake's vector instead of Real's). When this program is compiled, -1 is returned instead of 4.
#include <iostream>
#include <vector>
#include <gmock/gmock.h>
using namespace std;
//using ::testing::_;
using ::testing::Invoke;
class A {
protected:
vector<int> a;
public:
virtual int first() = 0;
virtual ~A() {}
};
class Real : public A {
public:
virtual int first() {
cout << "size: " << a.size() << endl;
return a[0];
}
};
class Fake : public A {
public:
virtual void insert(int b) {
a.push_back(b);
cout << a.size() << endl;
}
private:
virtual int first() { return -1; }
};
class Mock : public A {
public:
Mock(Fake* c) :
c_(c) {}
MOCK_METHOD0(first, int());
void delegate() {
ON_CALL(*this, first())
.WillByDefault(Invoke((Real*)c_, &Real::first));
}
private:
Fake* c_;
};
int main(void) {
Fake c;
c.insert(4);
Mock z(&c);
z.delegate();
cout << z.first() << endl;
return 0;
}
Does anyone know what I'm doing wrong? Or is there an easier way to accomplish this?
Upvotes: 5
Views: 7206
Reputation: 2190
This breaks the first requirement a bit, but the amount of boilerplate added to make testing work is minimal.
Separate the storage from A and the mock into it's own class and inject it in the constructor.
class AStorage {
vector<int> vals;
};
class A {
AStorage& storage;
public:
A(AStorage& stor) :
storage(stor) {}
};
Unfortunately, A can't own its own storage. However, testing is now easier because the test will have a reference to the storage object, allowing it to change behind the data behind the scenes.
Upvotes: 0
Reputation: 4314
The implementation instance c
given to Mock
is a Fake
object, and no amount of casting of a pointer to it will ever make it a Real
. Because both Fake
and Real
derive first()
from A
, the Fake
object's first()
is successfully called even though the compiler thinks it calls the first()
function of a Real
.
It's not so easy to tell what it is you want, because your example is so minimal (and mocking isn't really necessary in that minimal case), but there are multiple ways to go about achieving your desired behavior, a few off the top of my head:
Provide a default definition of first
in A
and remove the definition from Fake
:
class A {
protected:
vector<int> a;
public:
virtual int first() {
return a[0];
}
virtual ~A() {}
};
class Fake : public A {
public:
virtual void insert(int b) {
a.push_back(b);
cout << a.size() << endl;
}
};
Working definition of first
in Fake
. I know, I know, this code doesn't belong in the Fake
, but it's a quick fix for such a trivial case.
class Fake : public A {
public:
virtual void insert(int b) {
a.push_back(b);
cout << a.size() << endl;
}
private:
virtual int first() {
return a[0];
}
};
If all of this is about not invoking the printout in the A
's first()
, i suggest you mock out the out stream instead.
Upvotes: 2