I have an issue regarding how one would go about designing an application suited for unit-testing.
I am trying to implement the SRP (Single-Responsibility Principle), and from what I understood this involves splitting out most functionality in seperate, dedicated classes to keep code more organised. For example, I have this specific scenario.
A class RecurringProfile, which has a method .ActivateProfile(). What this method does is mark the status as activated, and create the next (first) recurring payment for the next due date. I was going to split out the functionality to create the next recurring payment in a seperate class, for example RecurringProfileNextPaymentCreator. My instant idea is to have this class take the 'RecurringProfile' as a parameter in it’s constructor:
RecurringProfileNextPaymentCreator(IRecurringProfile profile)
However, I think this would be problematic for unit-testing. I would like to create a unit-test which tests out the ActivateProfile functionality. This method would get an instance of an IRecurringProfileNextPaymentCreator via dependency injection (Ninject), and call the method .CreateNextPayment.
My idea to create a unit-test was to create a mock of an IRecurringProfileNextPaymentCreator, and substitue that so that I can verify that the .ActivateProfile() actually called the method. However, due to the constructor parameter, this would not fit as a default constructor for NInject. Having to create a custom NInject provider just for such a case (where I can have many such classes all over the solution) would be a bit overkill.
Any ideas / best practices how one would go about this?
—
Below is a sample code regarding the above example: (Please note that the code is hand-written, and is not syntactically 100% correct)
public class RecurringProfile
{
public void ActivateProfile()
{
this.Status = Enums.ProfileStatus.Activated;
//now it should create the first recurring payment
IRecurringProfileNextPaymentCreator creator = NInject.Get<IRecurringProfileNextPaymentCreator>();
creator.CreateNextPayment(this); //this is what I'm having an issue about
}
}
And a sample unit-test:
public void TestActivateProfile()
{
var mockPaymentCreator = new Mock<IRecurringProfileNextPaymentCreator>();
NInject.Bind<IRecurringProfileNextPaymentCreator>(mockPaymentCreator.Object);
RecurringProfile profile = new RecurringProfile();
profile.ActivateProfile();
Assert.That(profile.Status == Enums.ProfileStatus.Activated);
mockPayment.Verify(x => x.CreateNextPayment(), Times.Once());
}
Going up to the sample code, my issue is whether it is a good practice to pass over the RecurringProfile as a parameter to the creator.CreateNextPayment() method, or whether it makes more sense to somehow pass the RecurringProfile to the DI-framework, when getting an instance of an IRecurringProfileNextPaymentCreator, considering that the IRecurringProfileNextPaymentCreator will always act on an IRecurringProfile to create the next payment. Hope this makes the question a bit more clear.
As you did not show any code I’m guessing that you want to do something like this
Isn’t that simple enough?
Update
Don’t abuse a DI container as a ServiceLocator. Your idea to inject the payment creator as ctor parameter is the right way to go. ServiceLocator is considered an anti-pattern in modern application architecture. Something like the code below should work fine.