2

I know I can't use Moq to mock out a static method call inside my method under test, so what would I need to do to refactor the method so I can test it? I also have a method calling the base class method, would I need to refactor that and if so how? I don't want to use MS.Fakes or TypeMocks and create a shim, I would rather refactor and write solid code!

    public override DateTime ResolveDate(ISeries comparisonSeries, DateTime targetDate)
    {
        if (comparisonSeries == null)
        {
            throw new ArgumentNullException("comparisonSeries");
        }

        switch (comparisonSeries.Key)
        {
            case SeriesKey.R1:
            case SeriesKey.R2:
            case SeriesKey.R3:
            case SeriesKey.R4:
            case SeriesKey.R5:
                return DateHelper.PreviousOrCurrentQuarterEnd(targetDate);
        }

        return base.ResolveDate(comparisonSeries, targetDate);
    }

    [TestMethod]
    public void SomeTestMethod()
    {
        var mockIAppCache = new Mock<IAppCache>();
        var mockISeries = new Mock<ISeries>();

        ReportFR2 report = new ReportFR2(SeriesKey.FR2, mockIAppCache);
        DateTime resolvedDate = report.ResolveDate(mockISeries, DateTime.Now);

        //Assert.AreEqual("something", "something");

    }
chuckd
  • 13,460
  • 29
  • 152
  • 331
  • Or do I want to call the static method in my method under test? I think the answer is no, because a unit test only tests the logic in the method under test and nothing else, unless its a private method. Somebody correct me if I'm wrong! – chuckd Jun 21 '13 at 23:45
  • 2
    Wrap DateHelper with an interface and inject your IDateHelper as a method parameter in the containing class, or as a constructor argument, just like you're doing withIAppCache? – Mathieu Guindon Jun 21 '13 at 23:45
  • What would be better? 1) wrap the DateHelper with an interface and pass it in or 2) isolate the static method in "protected internal virtual" method in the class, then I could mock out the method. – chuckd Jun 22 '13 at 00:02
  • Depends on the implementation. If DateHelper has some methods you'd want to use in your test scenario, just setting what Now is in the method you call directly, virtual method might be a good choice. I'd err on the side of the interface though. – Trey Mack Jun 22 '13 at 00:30
  • If I "err" on the side of an interface I would have to refactor my class to a instnace class from a static class right? Or is there another way? – chuckd Jun 22 '13 at 01:35
  • Yep, that's the suggestion. Non-static. If there are constraints that make this undesireable, there may be another way. – Trey Mack Jun 22 '13 at 01:58

2 Answers2

2

Looking at the above, you have three basic conditions you can test:

  1. When Comparison Series is null

  2. When the Comparison series key is R1:R5

  3. When the Comparison series key is not null and anything but R1 : R5

In condition 1, you can cover this with a test pretty easily.

In condition 2, When it's R1:R5, that is where it appears it hits your static method.

  • From the perspective of your ResolveDate method, you still care what the value is when it hits this branch.
  • It would be nice to be able to prove that R1:R5 call the static helper, but as mentioned in the comments, the only way to cleanly do that is to wrap your DateHelper with an interface and pass that to the constructor of this class. That may not be worth the effort.
  • If you decide not to do that, I suggest that you still give a test that falls into that branch, and then also write another set of tests that targets your DateHelper.PreviousOrCurrentQuarterEnd() function directly, hitting all the edge cases. This will help isolate which code is the culprit if something fails.

The same can be said for condition 3 as condition 2. Although it's in your base class, it's still a valid logic branch.

  • Again, you will have a hard time proving that it called your base class,

  • But it is still valid to check the outcome.

So, I think you have four sets of tests you can write to start out, and then after you have these passing, you can decide if you want to take on refactoring your utility class DateHelper. My guess is you'll say no :-D

  1. Given a ReportRF2 class and a null Comparison series

    • When calling report.ResolveDate

    • It should throw a Null reference exception. Use
      `Assert.Throws( () => report.ResolveDate( null, DateTime.Now ));

  2. Given a ReportRF2 class and a series key in the set of R1:R5

    • When Resolving the Date for boundary X (such as 1/1/0001), it should equal y;

    • When ".." for ..., ...; (repeat for your edge/boundary cases; consider using Data Driven)

  3. Given the ReportRF2 class and a series key NOT in the set of R1:R5 and NOT NULL

    • when Resolving the Date for boundary X, ... similar to #2 but probably different expected results.
  4. Given the static utility class DateHelper

    • when calculating the PreviousOrCurrentQuarterEnd() and the date is X, it should equal y,
    • similar to edge cases in #2 above.

That will then give you coverage of the expected results, and tells you failures are stemming from your ResolveDate method or your DateHelper.PreviousOrCurrentQuarterEnd() method. It may not isolate as much as a purist would like, but as long as you cover your edge cases and your happy paths, it proves your app is functioning as planned (as long as those tests are passing).

What it does not really let you do is assert that a specific behavior is taken, other than when the comparison series is null, so it's up to you to decide if you need that verification. But, you should still have proof that when certain values or ranges go in, you get predictable outputs, and that adds some value.

Damon
  • 1,249
  • 1
  • 15
  • 27
1

Just to add to @Damon's nice answer, wrapping the DateHelper with an interface can be done quite easily:

public interface IDateHelper
{
    DateTime PreviousOrCurrentQuarterEnd(DateTime targetDate);
}

As noted, you'll need an instance class that implements this interface, but only for your production code, since unit tests will just use a Mock<IDateHelper:

public class InstanceDateHelper : IDateHelper
{
    public DateTime PreviousOrCurrentQuarterEnd(DateTime targetDate)
    {
        return DateTimeHelper.PreviousOrCurrentQuarterEnd(targetDate);
    }
}

Voilà, you can now mock the IDateHelper interface and you have an implementation that uses the existing static code.

I've used this wrapping technique for writing unit tests on a method that launches a new Process, so I could test the method without actually launching a full-fledged process when all I needed to know was whether the method under test would call .Start(StartInfo), without the side-effects.

Picture this method:

public bool StartProcessAndWaitForExit(ProcessStartInfo info)
{
    var process = Process.Start(info); // test-hindering static method call
    //...
}

The only change I had to make was this:

public bool StartProcessAndWaitForExit(IProcessWrapper process, ProcessStartInfo info)
{
    var process = process.Start(info); // injected wrapper interface makes method testable
    //...
}

If ResolveDate is the only method in your class that requires an IDateHelper, injecting it as a method parameter is fine; if you have a bunch of methods that would all need it as well, injecting it as a constructor argument and making a private readonly IDateHelper _helper; field (initialized in the constructor) is the best way to go.

Mathieu Guindon
  • 69,817
  • 8
  • 107
  • 235