So you’re a TDD developer. You code along merrily, writing failing unit tests, implementing them to a point of success, and then you refactor. But then, with no notice–WHAM–a private method. Now what!?
There are different schools of thought for this scenario. One camp says that if it’s not publicly exposed, you shouldn’t be testing it. I think that’s valid, but it’s equally valid to write test code for private methods. Just because a method’s private doesn’t mean it doesn’t deserve the same special attention. And just because I want to test a method that happens to be private doesn’t mean I want to expose it to the rest of the world! Let’s not worry about the debate for now. I’m going to show you how you can override private methods with Microsoft Fakes and how call private methods with Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject.
Override private methods
Problem: You have a private method with behavior that’s disruptive to unit testing. Regardless of the cause of the disruption, you want to provide an alternate implementation.
Solution: Use Microsoft Fakes to shim the private method. (Need to know more about Fakes? Check out my tutorial!)
Here’s how you can do it:
- Create an instance of your class
- Create a shim from your class
- Override the private method on the shim
- Invoke the test method
Here’s a code example:
[TestMethod] public void SecretMethod_FunctionalityIsOverridden() { using (ShimsContext.Create()) { var target = new FileReader("foo"); var shim = new ShimFileReader(target); bool calledShimMethod = false; shim.SecretMethod = () => { calledShimMethod = true; return "bar"; }; target.Read(); Assert.IsTrue(calledShimMethod); } }
Call private methods
Problem: You want to test a private method, but you can’t because it’s private.
Solution: Use Microsoft Test Framework’s included PrivateObject class.
The only thing I don’t like about PrivateObject is that it (presumably) uses reflection for method names, which means method names will be coded as strings with no compile-time checks. It’s very easy to use, though, and if you need to invoke a private method, I haven’t seen a better way to do it!
Here are the steps:
- Create an instance of your class
- Create a PrivateObject from your class
- Use the PrivateObject‘s Invoke method
And here’s the code:
[TestMethod] public void SecretMethod_ReturnsHumorousMessage() { var target = new FileReader("foo"); var privateObject = new PrivateObject(target); var actual = privateObject.Invoke("SecretMethod"); Assert.IsNotNull(actual); }
Putting it all together (useless, but fun!)
I was able to verify both of these capabilities by writing a single unit test that overrides the private method, then calls the private method. Check it out:
[TestMethod] public void SecretMethod_FunctionalityIsOverridden() { using (ShimsContext.Create()) { var target = new FileReader("foo"); var shim = new ShimFileReader(target); // override the private method bool calledShimMethod = false; shim.SecretMethod = () => { calledShimMethod = true; return "bar"; }; // call the private method var privateObject = new PrivateObject(target); var actual = privateObject.Invoke("SecretMethod"); // assert that calling the private method // used the overridden implementation Assert.IsTrue(calledShimMethod); } }
Hi Adam, override private method not work.
The private method not exist in shim class.
Are you sure about this code ?
Yes, I’m sure about it. Can you post your example that doesn’t work?
As I read in this link: http://msdn.microsoft.com/en-us/library/hh549176.aspx
Has limitations: “The Fakes code generator will create shim properties for private methods that only have visible types in the signature, i.e. parameter types and return type visible.”
Because that I can see my private method
Thanks anyway. Help me to understand
Hello,
“Override private methods” is not working for me either. Tried to perform an easy test with a dummy class. The test method runs, but doesn’t bypass the original implementation and throws an exception (the one I left on purpose).
Please note the code for better understanding:
public class DummyClass
{
public int PublicMethod()
{
var i = PrivateMethod();
return 1;
}
private int PrivateMethod()
{
throw new NotImplementedException();
}
}
//Unit Test Class in separate project
public class DummyClassTest
{
[TestMethod]
public void PublicMethod_BypassPrivateMethod()
{
using (ShimsContext.Create())
{
// Arrange
var called = false;
var shimDummyClass = new ShimDummyClass();
shimDummyClass.PrivateMethod = () =>
{
called = true;
return 100;
};
var sut = new DummyClass();
// Act
sut.PublicMethod();
// Assert
Assert.IsTrue(called);
}
}
}