I'm working on a project where there is a lot of external service messaging. A good way to describe it in only a slightly "hyperbolas" way would be an application where the system has to send messages to the Flicker API, the Facebook API, and the Netflix API.
To support disconnected scenarios, logging concerns, developer usability, configuration, etc... I've experimented using an approach which heavily uses generics and expression trees. The end result looks like this:
Messenger<NetflixApi>.SendCustom( netflix => netflix.RecommendMovie("my message"));
Overall I'm happy with the end result but feel like I've made a mistake, or overlooked a design principal someplace with regards to testing and disconnected scenarios.
During testing, whether automated, unit, or human based, I've implemented an object factory that initially uses DI to perform the correct action in "Live mode" and used Mocks to provide a sort of sterile messenger that doesn't do anything at all when in a testing mode.
I've only seen or read about Mocks being used in pure TDD mode and not being used to be sort of a dumb object. The approaches I've seen would revolve around stubbing or mocking out the HTTP communication functionality which all the APIs I'm using depend on.
My main concern is that will all the different services I expect to connect to I'd end up having to do a lot of granular work substituting specific HTTP implementation and if I used a stub approach I'd have 3 classes for each of these services( IService, ConcreteService, StubService ) and maintaining those when implementing a new method or changing anything would be a real PITA.
In the current implementation I'm using Mocks to get "sterile mode" for free almost without having to implement anything extra just to conform to a certain testing principal.
The question is am I missing something? Did I violate a design principal using Mocks in a more... convenient way?
Can anybody offer any advice on how to get a sterile mode out of many different outside services without jumping through a lot of hoops?
Does this question make sense?
Thanks for all the answers.
Edit #1:
I wasn't clear in my original question. Any null or mock objects are to be used purely in a development/debug/testing environment. In production the code that sends these messages will be the actual implementation of them.
I voted everybody up because there seem to be a lot of different solutions to this problem and I'll be exploring each one.
Please don't think this question has been answered yet, I'd appreciate as much advice as I can get.
Only use a mock (or test double) “when testing things that cross the dependency inversion boundaries of the system” (per Bob Martin). If I truly need a test double, I go to the highest level in the class hierarchy diagram above that will get the job done. In other words, don't use a mock if a spy will do.
Mocking is a very common testing mechanism, and it is a bad idea. This post details why you should not use mocking, and why and how you should write integration tests instead. TL;DR: Mocking provides false confidence by hiding real failures.
Mock Object Drawbacks You are coupling your test to a specific implementation and behavior of the object under test. You tend to have more tests fail when they shouldn't when mocking. This makes these tests brittle.
Stub - an object that provides predefined answers to method calls. Mock - an object on which you set expectations. Fake - an object with limited capabilities (for the purposes of testing), e.g. a fake web service. Test Double is the general term for stubs, mocks and fakes.
There is a design pattern called Null Object. A null object is an object that implements a Interface, so it could be used in an scenario like yours.
The important thing about the Null Object is that DON'T return null in places where that could break the system.
The purpose of the Null Object is to have a void and simple implementation of something, just like a Mock, but to be used in the production environment.
The most simple example is something like this:
class Logger{
private static ILogger _Logger;
static Logger(){
//DI injection here
_Logger = new NullLogger(); //or
_Logger = new TraceLogger();
}
}
interface ILogger{
void Log(string Message);
}
internal class TraceLogger:ILooger{
public void Log(string Message){
//Code here
}
}
internal class NullLogger{
public void Log(string Message){
//Don't don anything, in purporse
}
}
I hope this could help you
I think you may need to clarify your question. I'm unclear as to whether you are talking about using test doubles in testing without stubbing or testing expectations (so using them as fakes to meet required interfaces) or whether you are talking about using mocks in a production scenario to fill in for services that are not available (your disconnected scenario).
If you are talking about in test situations: Mocks are test doubles. There's nothing wrong with testing using fakes as opposed to mocks or stubs. If you don't need to use the service in a specific test and it is just there to provide a given interface that the object under test has a dependency on then that it fine. That is not breaking any testing principal.
Good mocking libraries are blurring the lines between mocks, stubs and fakes.
Have a look at some of the information from Martin Fowler on the different types of test doubles. Mocks Aren't Stubs, TestDoubles.
I really like the way that moq allows a continuum between dummy, fake, stub and mock objects without you needing to jump through hoops to get specific behaviors. Check it out.
If you are talking about using mocks in your disconnected scenario for production use... I'd be worried. A fake is going to return null objects or default values for any calls that you make. This will leak complexity into all of the code consuming these services (they'll need to handle checking for null return values and empty arrays...). I think it would make more sense for your disconnected / sterile scenario to be coded to handle that the dependencies themselves are unavailable rather than accepting mock implementations of them and continuing as if everything is working.
It sounds to me like you may want to have a look at the proxy pattern. You want something that acts like various services even when disconnected from the actual services. Therefore your code would be better off talking to a proxy instead of the real thing. Then the proxy can do whatever it needs to depending on the current connection status.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With