Reputation: 371
I am trying to test a method that has a using statement that creates a new concrete instance of the IDisposable type.
To do this I am trying to fake a private method that is executed in the constructor of an IDisposable type. I could change the code so that it was more easily testable, but I would like to know if this way is possible first.
So far I have got:
An example code that illustrates my problem
namespace FakeUsing
{
using System;
using System.Linq.Expressions;
using System.Runtime.Remoting.Messaging;
using FakeItEasy;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Reflection;
// Application under test
class Program
{
public bool success = false;
// Entry Point
static void Main(string[] args)
{
Program p = new Program();
p.Run();
}
// Method I would like to test
public void Run()
{
using (new Concrete("data"))
{
// Code I actually want to test.
success = true;
}
}
}
// Problem class that I cannot change
public class Concrete : IDisposable
{
public Concrete(string data)
{
PrivateCall();
}
private void PrivateCall()
{
// Some stuff that uses unmanaged resources
// I'm not interested in testing
DoStuff();
}
public void Dispose()
{
// Some stuff that uses unmanaged resources
// I'm not interested in testing
DoStuff();
}
private void DoStuff()
{
throw new NotImplementedException();
}
}
// Testing code
[TestClass]
public class UnitTests
{
[TestMethod]
public void TestRun()
{
// Arrange
Program p = new Program();
MethodInfo doStuffMethodInfo =
typeof(FakeUsing.Concrete)
.GetMethod("DoStuff", BindingFlags.Instance | BindingFlags.NonPublic);
// This is the line I cannot get to work.
MethodCallExpression doStuffMCE =
Expression.Call(???, doStuffMethodInfo, null);
A.CallTo(doStuffMCE).DoesNothing();
// Act
p.Run();
// Assert
Assert.IsTrue(p.success);
}
}
}
In FakeUsing.UnitTests.TestRun I cannot get around the creating the MethodCallExpression instance a I don't have the instance before hand.
Thanks
David
Upvotes: 3
Views: 1428
Reputation: 241714
This is not possible. FakeItEasy will only be able to intercept methods on fakes that it's created, and you're new
ing up the production class to test.
Even if it were possible, I'd advise against it. Mocking private
methods often leads to (at best) over-specified, brittle tests. As soon as the implementation changes, all the tests break. This is especially dangerous if (as you hint), you don't have control over the class that has the private method.
I think a better approach would be to change the production code (that you can change) to use a factory of some sort to return an IDisposable
. Then provide an implementation of the factory that returns a do-nothing object. This should be quite straightforward with FakeItEasy. (Or even without it! The behaviour is simple enough that you wouldn't need a mocking framework if you didn't already have one.)
Upvotes: 2