TDD_hobbyist
TDD_hobbyist

Reputation: 23

Coding to an interface without an additional use of the interface aside from mocking for tests?

I have been trying to apply TDD at work as of late and occasionally get hung up when I need to think about the best ways to mock. What I am working on involves a good deal of network requests and file system access so there doesn't seem to be anyway around mocking. From the GMOCK documentation my understanding is that my best options to mock classes are either through, "coding to an interface", or via templates.

Here are my 2 main questions regarding "coding to an interface", which afterward I will give examples to hopefully clarify what I am asking:

  1. When I currently have no additional uses of an interface beside mocking/testing, does it still make sense to use this method?
  2. So now if I go ahead with "coding to an interface", which I only did for mocking/testing. If instead of passing around that interface, I just pass around what I want to mock(who inherits from that interface), am I still "coding to an interface"? I bring this up because sometimes this feels more readable.

Sample code for #1...

class Thing {
  bool IsInstalled(); // multiple filesystem calls, I want to mock this method
};

bool Install(vector<Thing>& thingsToInstall) // before "coding to an interface"
bool Install(vector<unique_ptr<IInstallable>>& thingsToInstall) // after "coding to an interface"

class Thing : public IInstallable {
  bool IsInstalled() override;
};

class IInstallable {
  virtual bool IsInstalled() = 0;
};

The behavior I am interested in mocking is whether or not these "Thing"s are installed or not. Based on that I may or may not do a network request(which I've already mocked). So instead of passing around a vector of "Thing" objects, I have to pass around a vector of unique_ptr to IInstallable(the behavior/interface I want to mock). It seems like a lot just so I can only test it and not even reuse the interface. I guess I want to know am I indeed "coding to an interface" here and this is just the way it is? Also could you elaborate on when it does and does not make sense to "code to an interface"?

Sample code for #2...

  bool Install(vector<unique_ptr<IInstallable>>& thingsToInstall) // original "coding to an interface" method from above

So now that I am coding to an interface, seeing as how my only reason for passing around unique_ptrs to the interface was so I could test it. Is there any reason why I shouldn't just pass around unique_ptrs to my "Thing" which implements the IInstallable interface? I can then still directly inherit from "Thing" for my mock class. This also in my code feels more readable since I am passing around a type that feels more relevant to the surrounding code(Thing) instead of a more generic interface. But then am I even "coding to an interface" anymore? Or am I just completely destroying the purpose(besides testing) of "coding to an interface"?

This is what the final method would look like...

  bool Install(vector<unique_ptr<Thing>>& thingsToInstall) // Thing still implements IInstallable, so I can mock Thing directly

Thank you anyone that takes the time to read this and respond! I greatly appreciate your help.

Upvotes: 1

Views: 121

Answers (1)

Mine
Mine

Reputation: 4241

I think your main question is about this argument:

It seems like a lot just so I can only test it and not even reuse the interface.

But your coding to interface example seems not practice. Defining an interface really requires careful design, rather than "I want to mock it so it should be an interface".

So back to your example, what you should really consider is, what is the common interface for class Thing, yes it should have bool IsInstalled(); but what else?

Let's say your real class Thing has two function in practice, then you're going to define your interface like this:

class IThing {
  virtual bool IsInstalled() = 0;
  virtual void DoSomething() = 0;
};

And then you can implement it:

class Thing : public IThing{
  bool IsInstalled() override;
  void DoSomething() override;
};

And use it like this:

bool Install(vector<unique_ptr<IThing>>& thingsToInstall);

Now you have the opportunity to:

  1. Use IThing in your functions;
  2. Create Thing and pass IThing to the functions in your production code;
  3. Create MockIThing and pass IThing to the functions in your test code.

Upvotes: 1

Related Questions