Reputation: 9041
I'm trying to write a unit test to test a protected method in an abstract class. I've tried writing a test class that inherits from the abstract class, but when I instantiate the test class the base abstract class attempts to connect to an Oracle database and fails which doesn't allow me to test the protected method I'm interested in. The abstract class cannot be modified.
How can I directly unit test a protected method in this abstract class?
Here is snippet of what I tried with reflection.
Type type = typeof(AbstractClass);
BindingFlags eFlags = BindingFlags.Instance | BindingFlags.NonPublic;
MethodInfo myMethod = type.GetMethod("ProtectedMethod", eFlags);
object[] arguments = new object[] { _myDs };
myMethod.Invoke(type, arguments);
_myDs = (DataSet)arguments[0];
Upvotes: 1
Views: 6911
Reputation: 10350
It is doable, but please note there may be couple of serious problems with your code:
Here's a recipe:
FormatterServices.GetUninitializedObject
for getting instance of the class without calling constructor.ProtectedMethod
.By that, you are able to test ProtectedMethod
:
And here is an example code:
class AbstractClassFake : AbstractClass { }
[Test]
public void Test()
{
// Arrange:
var abstractClassFake = (AbstractClassFake)FormatterServices
.GetUninitializedObject(typeof(AbstractClassFake));
MethodInfo method = abstractClassFake.GetType()
.GetMethod("ProtectedMethod",
BindingFlags.Instance | BindingFlags.NonPublic);
object[] arguments = new object[] { myDs };
// Act:
object val = method.Invoke(abstractClassFake, new[] { myDs });
// Assert:
// TODO: Your assertion here
}
Upvotes: 3
Reputation: 3665
Compromise.
Say you have:
public abstract class CanNeverChange
{
public CanNeverChange()
{
//ACK! connect to a DB!! Oh No!!
}
protected abstract void ThisVaries();
//other stuff
}
public class WantToTestThis : CanNeverChange
{
protected override void ThisVaries()
{
//do something you want to test
}
}
Change it to this:
public class WantToTestThis : CanNeverChange
{
protected override void ThisVaries()
{
new TestableClass().DoSomethingYouWantToTest();
}
}
public class TestableClass
{
public void DoSomethingTestable()
{
//do something you want to test here instead, where you can test it
}
}
Now you can test the behavior you want to test in TestableClass
. For the WantToTestThis
class, compromise to the pressure of terrible legacy code, and don't test it. Plugging in a testable thing with a minimal amount of untested code is a time honored strategy; I first heard of it from Michael Feather's book Working Effectively with Legacy Code.
Upvotes: 1
Reputation: 6123
I think the best solution is to break the class apart as the comments suggest.
However, if you would like to test it without changing the class very much, you could move the contents of the constructor into a virtual method:
public abstract class TheOracleOne
{
protected virtual void Init()
{
// connection things
}
}
And override in your derived class to do nothing:
public class TestClass : TheOracleOne
{
protected override void Init()
{
}
}
This should skip the initialization that is taking place in the abstract class's constructor, allowing you to access the method normally on the derived class.
Upvotes: 0