-1

I'm attempting to isolate and test a particular class in an existing code base which is derived from some base and has a private class member that I want to mock:

public class Derived : Base 
{ 
   protected Something<Other> _otherSomething = new Something<Other>();
}

Specifically, I'd like to use the tests to verify that Derived raises some event DoSomething in response to _otherSomething raising the ItHappened event.

In an attempt to accomplish this, I created in my test project a MockedDerived class which is a Derived:

public class MockDerived : Derived
{
   public Something<Other> OtherSomething 
   { 
      set { _otherSomething = value; } 
   }
}

I'm using NSubstitute as part of my MSTest test method to mock out a class and I want to do something like so:

[TestMethod]
public void TestMethod1()
{
   var sub = Substitute.For<Something<Other>>();
   MockedDerived md = new MockedDerived();
   bool wasRaised = false;
   md.DoSomething += (sender, args) => wasRaised = true;
   md.OtherSomething = sub;
   sub.itHappened += Raise.Event<ItHappenedEventHandler<Other>>(new ItHappenedEventArgs<Other>());
   Assert.IsTrue(wasRaised);
}

Where I'm having some difficulty is in that there is a fair amount of baggage that the Base class brings to the table, namely it creates some objects of other classes and those have some timers and ultimately involve a database.

It gets messy.

And all I really want to do is ensure that this Derived class exhibits particular behaviors and responds as expected.

Any suggestions on how to isolate the testing of this Derived behavior from the messy Base and its various members? Perhaps I'm just not seeing the obvious solution.

itsmatt
  • 31,265
  • 10
  • 100
  • 164

2 Answers2

1

You are facing the situation of testing legacy code where your base class takes in concrete dependencies rather than depending on interfaces. You might want to refer to the Sprout method and Sprout classes described in Michael Feather's book "Working Effectively with Legacy Code".

Phillip Ngan
  • 15,482
  • 8
  • 63
  • 79
1

Without knowing the details of what your code is doing or how much flexibility you have to change what is there, I can think of two rough approaches to try: isolating both Base and Derived from their common dependencies, or breaking the inheritance chain.

As an example of the former, we could inject the "baggage" causing the problem into Base (we'd have to provide that to Derived as well). We can then fake that out in a test to keep our test free from timers and databases.

public class Base {
  public Base(DatabaseStuff d, TimerStuff t) { ... }
}
public class Derived : Base {
  public Derived(DatabaseStuff d, TimerStuff t, Something<Other> something) : base(d,t) {...}
}

For the second option we could try replacing inheritance with composition. Rather than inheriting from Base, we could make Derived have a reference to a Base and delegate to that instead.

public class Derived {  //TODO: Rename to `NotSoDerived` 
  public Derived(Base b, Something<Other> something) { ... }
  public ItHappenedEventHandler<Other> itHappened ...;
  public void SomeBaseMethod() {
    b.SomeBaseMethod(); // let b handle this call
  }
}

Now we can inject a fake Base (or null <shudder> if your test won't call Base) and a mocked Something<Other>, and we can test that aspect of Derived in isolation.

If you need to use Derived and Base interchangeably then you could look at extracting an IBase interface if that makes sense, and then both Base and Derived could implement that.

David Tchepak
  • 9,826
  • 2
  • 56
  • 68
  • Thanks for the response - without going into too much detail, Derived was developed as a replacement/wrapper of Base to provide some caching of of data, etc. It overrides a subset of the functions provided by Base - a minority of the functions. I guess the downside to the composition route is that each of those functions requires a delegating function in **NotSoDerived** but the upside of this, as you say, is that testing NotSoDerived is simplified because the faked out Base implementation is lightweight and neither knows nor cares about any of that DatabaseStuff and TimerStuff. – itsmatt Apr 16 '14 at 14:00