Well, you can achieve the same clean way using Abstract Factory Pattern though (maybe not suitable to call it Abstract Factory Pattern).
Example in C#:
public class CardChargerWrapper{ public CardChargerWrapper(CardCharger charger , NullCardCharger nullCharger , TestUnitConfig config){ // assign local value this.charger = new CardCharger(); } CardCharger charger; NullCardCharger nullCharger; TestUnitConfig config; public Response Charge(CreditCard card, Money amount){ if(!config.IsUnitTest()) { return charger.connect().charge(card, amount); } else { return NullCardCharger.charge(card, amount); } } } EDIT: Changed CardChargerWrapper to use hard-coded instance of CardCharger instead of injecting it.
Note: You can change NullCardCharger to something like MockCardCharger or OfflineCardCharger for logging purpose.
Note again: You can change the CardChargerWrapper's constructor to fit. Example, instead of constructor injecting the NullCardCharger, you can make it property injected. Same with TestUnitConfig.
EDIT: Regarding calling IsUnitTest() a good idea or not:
It really depends on your business perspective and how you are doing testings. As many people said, a code that has not yet been tested is not trusted for their correctness. It cannot be reliabled. On side note, I prefer IsChargeRealCard() will be more suitable than IsUnitTest().
Say that we take out the unit test in our context, at least you will still need to do integration testing in test environment. You probably want to test something like:
- I want to test the credit card validation (is it real or not, etc).
- I want to test the payment method and see whether the card being charged. As a process, but not as a real credit card payment.
For 2nd point, I think the best thing to do is to create a mock credit card charger to log the transaction. This is, to ensure that the charging is correct. And it will be conducted in both test and dev server.
So, How can the CardChargerWrapper help such situation?
Now with CardChargerWrapper, you can:
- Switch the
NullCardChargerto any mock card chargers to enhance your unit testing. - All class using
CardChargerWrappercan be ensured that they are checkingIsUnitTestfirst before charging real card. - Your developer need to use
CardChargerWrapperinstead ofCardCharger, preventing development error for unit tests. - During code review, you can find whether
CardChargerbeing used in other class instead ofCardChargerWrapper. This is to ensure no leak of code. - I'm unsure, but seems like you can hide references of your main project to your real
CardCharger. This will protect your code further.