Adventures in C# Unit Testing

  • Thread starter Kenneth Cochran
  • Start date
K

Kenneth Cochran

It has become painfully obvious to me that I lack enough knowledge of C#'s
capabilities and limitations to coheres the project I'm working on into a
test framework. So I'm starting this thread to document my attempts and beg
for the occasional tidbits of help from the C# development community. I hope
this will prove useful to others who read this thread as well. The project I
inherited is several hundred thousand lines of code; all without unit tests.
I'm attempting to get the entire project under test making minimal changes
to do so. So without further ado:

public class ParentClass
{
private IMyInterface ProductionObject;
protected virtual IMyInterface createMyObject()
{
return new MyClass(this);
}
public ParentClass()
{
ProductionObject = createMyObject();
}
public Method()
{
...
ProductionObject.DoSomething();
...
}
}

ParentClass created ProductionObject in its constructor. ProductionObject is
later used by Method(). The first thing I did was extract creation to a
virtual method: createMyObject(). Continuing on...

public class ChildClass:parentClass
{
private IMyInterface mockMyObject;

protected override IMyInterface createMyObject()
{
return mockMyObject;
}

public ChildClass(IMyInterface mockMyObject)
{
this.mockMyObject = mockMyObject;
}
}

ChildClass overrides createMyObject to return a mock object created by the
ChildClass constructor. This is where things begin to derail. C# base
constructors are implicitly called by child constructors. The base
constructor correctly calls ChildClass's overridden createMyObject.
Unfortunately, since the child constructor has not yet finished,
mockMyObject is still undefined. The base constructor returns control to the
child constructor which assigns mockMyObject from its parameter but at this
point it's too late. When the test is is run on ParentClass.Method() it
accesses and undefined mockMyObject and throws an exception. Any
suggestions?
 
P

Peter Morris

That's one of the problems with C#, you can't tell it at which point in your
constructor to execute the base constructor. There are some things you can
do..

01: The base class could have the dependency passed
public ParentClass
{
public ParentClass(IMyInterface myInterfact)
{
//Check it's not null and store it away
}
}

If in general the IMyInterface implementor will always be the same class
then use Unity as a dependency injector. Then you only need to do this to
get an instance of ParentClass

var p = MyContainerHolder.Container.Resolve<ParentClass>();

the correct instance of IMyInterface will be constructed and passed in
automatically by Unity, and your tests can pass in a mocked IMyInterface.
This is the way I would do it, because this way you don't need to add
virtual methods etc to your ParentClass just to support testing, and when
you test you will be testing the real ParentClass and not a descendant.


02:
If you don't want to do it that way you could just lazy create your
interface..


public ParentClass
{
IMyInterface productionObject;
public IMyInterface ProductionObject
{
get
{
if (productionObject == null)
ProductionObject = CreateProductionObject();
return productionObject;
}
}
}
 
K

Kenneth Cochran

I went with the first suggestion. It allowed me to test the base class
directly. The project is a .NET 1.1 application so Unity was out of the
question. I could have gone with Spring.net, which does support 1.1 but I
didn't want to add another dependency to the project. Two unit tests down,
thousands to go...
 
K

Kenneth Cochran

I'm back already. This time I have a dependency on a static member function
of a system api class that I'm trying to isolate. Extract method and
override aren't any help. I extracted easily enough but all my previous
overrides replaced an object with a mock object. The mock framework I'm
using doesn't support mocking static classes or members so I think my only
option is to create a wrapper class for the dependency. Question is... do I
put it in production code or just in the unit test code? If anyone has an
alternative suggestion I'm all ears.
 
P

Peter Morris

What about doing a similar thing again?

//Standard implementation in app
public interface IMyApi
{
void ApiCallOne();
void ApiCallTwo();
}

public class MyApiImpl : IMyApi
{
public void ApiCallOne()
{
SomeStaticClass.ApiCallOne();
}

public void ApiCallTwo()
{
SomeStaticClass.ApiCallTwo();
}
}

//Now inject the use of it
public SomeDependentClass
{
readonly IMyApi MyApi;

public SomeDependentClass(IMyApi myApi)
{
//check not null
MyApi = myApi;
}

public void DoSomething()
{
MyApi.ApiCallOne();
MyApi.ApiCallTwo();
}
}


//Now when using it

var x = new SomeDependentClass(new MyApiImpl());
x.DoSomething();


To test it you would obviously pass a mocked interface.

To be honest I am not 100% keen on this. The reason I don't like it is the
line
var x = new SomeDependentClass(new MyApiImpl());

Everywhere you use the API you need to know that MyApiImpl uses it, this is
why I would normally use a dependency injection container.
 
K

Kenneth Cochran

Thanks for all your help so far. I have a general question about unit
tests. If I'm testing a method that has multiple paths, do I create a
separate test case for each path or test all paths in a single test case?
 
P

Peter Morris

Separate ones. The naming convention I go for is

MethodName_ExpectedBehaviour_Circumstances

So, for example

[TestMethod]
public void Publish_SetsReadOnly_WhenExecuted()
{
}

[TestMethod]
[ExpectedException(typeof(ValidationException))]
public void Publish_ThrowsException_IfObjectIsInInvalidState()
{
}

Think of the test cases like comments that describe the code *and* check the
description is accurate.
 
P

Peter Morris

PS: To clarify further. You are not testing your code, you are testing that
specific input produces expected output. Additionally branches of code in
your method merely mean more scenarios to test for and not a more
complicated single test scenario.
 
K

Kenneth Cochran

I've got a new one for you. I had a singleton class I was trying to get
under test. It had five public instance methods. After delving into each
method I discovered all the but the Init method either didn't depend on
instance data or delegated to a static method. It turns out the class was a
hybrid singleton/utility class. So I refactored it into a full utility
class, changing all instance references in the Init to static. I know
eventually I will pick it apart and move methods to where they are actually
used but in the mean time I still have to test it. Question is how?

Option 1: It's not to late to undo my refactoring and go the other way.
Change it to a normal class.

Option 2: Anyone know how to test static methods in isolation?

Option 3:?
 
K

Kenneth Cochran

Kenneth said:
It has become painfully obvious to me that I lack enough knowledge of
C#'s capabilities and limitations to coheres the project I'm working on
into a test framework. So I'm starting this thread to document my
attempts and beg for the occasional tidbits of help from the C#
development community. I hope this will prove useful to others who read
this thread as well. The project I inherited is several hundred thousand
lines of code; all without unit tests. I'm attempting to get the entire
project under test making minimal changes to do so. So without further ado:

public class ParentClass
{
private IMyInterface ProductionObject;
protected virtual IMyInterface createMyObject()
{
return new MyClass(this);
}
public ParentClass()
{
ProductionObject = createMyObject();
}
public Method()
{
...
ProductionObject.DoSomething();
...
}
}

ParentClass created ProductionObject in its constructor.
ProductionObject is later used by Method(). The first thing I did was
extract creation to a virtual method: createMyObject(). Continuing on...

public class ChildClass:parentClass
{
private IMyInterface mockMyObject;

protected override IMyInterface createMyObject()
{
return mockMyObject;
}

public ChildClass(IMyInterface mockMyObject)
{
this.mockMyObject = mockMyObject;
}
}

ChildClass overrides createMyObject to return a mock object created by
the ChildClass constructor. This is where things begin to derail. C#
base constructors are implicitly called by child constructors. The base
constructor correctly calls ChildClass's overridden createMyObject.
Unfortunately, since the child constructor has not yet finished,
mockMyObject is still undefined. The base constructor returns control to
the child constructor which assigns mockMyObject from its parameter but
at this point it's too late. When the test is is run on
ParentClass.Method() it accesses and undefined mockMyObject and throws
an exception. Any suggestions?

I'm back with yet another adventure in C# Unit Testing. I have two
classes which are mutually dependent:

public class A
{
private B b;

public A()
{
b = new B(this);
}
}

public class B
{
private A a;

public B(A a)
{
this.a = a;
}
}

My problem is I need to isolate the methods in A that rely on b. B is
already under test so I do have a small safety net. Normally I would
overload A's constructor like so:
public A():this(new B(this)){}

public A(B b)
{
this.b = b;
}
Of course it doesn't work because 'this' doesn't exist outside the body
of the constructor. I can't delegate A's constructor to a static factory
method for the same reason. What options do I have?

1. Delegate b's creation to a virtual method. Subclass and override. I'm
not too fond of this one. I prefer to test the production class instead
of an overridden child class.

2. Expose b as a public property using lazy initialization and remove
b's creation from the constructor all together.

3. Modify B instead, exposing a as a public property.

Any others?
 
K

Kenneth Cochran

Peter said:
[...]
My problem is I need to isolate the methods in A that rely on b.

Unfortunately, I don't really know what you mean here. You have given a
fair example of what I think is your goal, so hopefully that doesn't
matter.

Sorry I didn't explain clearly enough. I am working on getting several
million lines of code under a unit test framework. The project had no
unit tests in place. There are at least six classes in the project
similar to the example I gave. All are very complex and all have
parameterless constructors. I am isolating each method tested for
performance reasons as well as to avoid test cross-pollution.

I've accomplished this by overloading the constructor and it some cases
the public methods, parameterizing dependencies so I can provide
stub/fake/mock objects during test and the original parameterless
constructor or method delegates all work to the new one.

It has worked fine up to this point. However the constructor for B
requires a reference to A be passed. Apparently, inside the constructor
body creation has gotten far enough for the 'this' reference to actually
point to something.
In this scenario, where does "a" in your new instance of B get
initialized? Shouldn't it refer to the instance of A being constructed?

This is the problem I'm faced with. In C#, constructor delegation occurs
before the constructor body has been called and the 'this' reference
doesn't exist.
I'm not clear on how this solves the issue anyway, but regardless
calling virtual methods from constructors is dangerous and ill-advised.

This one is definitely a method of last resort. I've used this technique
in a pinch when parameterizing just wasn't practical but never for a
constructor. IMHO, it's a hack. Object initialization is delegated to a
protected virtual method. Then in the test, you subclass and override.
You can then provide any stub or mock object you want.
This could work. It seems reasonably elegant to me.

This appears to be the best option so far.
I'm not really clear on how this works into your unit testing. If
there's some way for you to explain the question where you can take the
unit testing out of the picture, but still sufficiently explain the
basic behavior of the classes you want, that might simplify the question
in a way to make it easier to answer.

Pete

The question is directly tied to unit testing. To test a class properly,
you need to isolate it from its dependencies. One way to do this is
using stubs/fakes/mocks. The easiest way to get your class to use them
is pass them in as parameters.
 
K

Kenneth Cochran

Peter said:
It seems to me that the problematic scenario is the scenario where
you're _not_ unit-testing, because that's when you're not passing
instances of dependency objects to the working object. In the
parameterized constructor scenario, when testing A you only need a mock
B, and you can initialize that B instance in A's constructor, passing
"this" to the mock B's constructor.

No?

I think I see what your getting at. I was trying to be clever and have
the original constructor call the parameterized constructor, which in
this one case doesn't compile.

In essence I was trying to fix a problem when I could have just avoided
it. So instead of this:

public A():this(new B(this)){}

public A(B b)
{
this.b = b;
}

I should use this:

public A()
{
b = new B(this);
}

public A(B b)
{
this.b = b;
}
 
K

Kenneth Cochran

Kenneth said:
It has become painfully obvious to me that I lack enough knowledge of
C#'s capabilities and limitations to coheres the project I'm working on
into a test framework. So I'm starting this thread to document my
attempts and beg for the occasional tidbits of help from the C#
development community. I hope this will prove useful to others who read
this thread as well. The project I inherited is several hundred thousand
lines of code; all without unit tests. I'm attempting to get the entire
project under test making minimal changes to do so. So without further ado:

public class ParentClass
{
private IMyInterface ProductionObject;
protected virtual IMyInterface createMyObject()
{
return new MyClass(this);
}
public ParentClass()
{
ProductionObject = createMyObject();
}
public Method()
{
...
ProductionObject.DoSomething();
...
}
}

ParentClass created ProductionObject in its constructor.
ProductionObject is later used by Method(). The first thing I did was
extract creation to a virtual method: createMyObject(). Continuing on...

public class ChildClass:parentClass
{
private IMyInterface mockMyObject;

protected override IMyInterface createMyObject()
{
return mockMyObject;
}

public ChildClass(IMyInterface mockMyObject)
{
this.mockMyObject = mockMyObject;
}
}

ChildClass overrides createMyObject to return a mock object created by
the ChildClass constructor. This is where things begin to derail. C#
base constructors are implicitly called by child constructors. The base
constructor correctly calls ChildClass's overridden createMyObject.
Unfortunately, since the child constructor has not yet finished,
mockMyObject is still undefined. The base constructor returns control to
the child constructor which assigns mockMyObject from its parameter but
at this point it's too late. When the test is is run on
ParentClass.Method() it accesses and undefined mockMyObject and throws
an exception. Any suggestions?

Ok, I stumbled upon this little gem while unit testing. Note: the
comments are not mine. The names of the functions have been changed to
protect the innocent...

private void RefreshSimpleListingDataset(SqlDataAdapter myAdapter,
DatasetSimple mySet)
{
try
{
sqlConnection.ConnectionString = _CONNECTION_STRING;
sqlConnection.Open();
myAdapter.Fill(mySet, _TABLE);
this._ListingSimple.Clear(); /* TODO:: shouldn't need this?
should update when preparers added/edited/deleted?*/
this._ListingSimple.Merge(mySet, true);
}
finally
{
sqlConnection.Close();
}
}

Ok, this guy is called after every method that updates a table. Why?
Because my predecessor(s) never bothered to learn the purpose of the
Dataset class. Take a guess at how the tables are being updated...

Every update is a SqlCommand linked to a stored procedure in the
database. Even the most trivial ones. Need to change the first name of
an employee? There's a _Update_Employee_First_Name procedure. So after
every update, the Dataset gets out of sync with the table(s) it
represents. Hence the above stupidity.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top