wavydavy
wavydavy

Reputation: 371

Using FakeItEasy A.CallTo with Instance MethodInfo without the instance

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:

  1. Use reflection to get the MethodInfo objects that point at the private methods
  2. Create an Expression type based on the MethodInfo object
  3. Tell FakeItEasy to DoNothing if that Expression is executed

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

Answers (1)

Blair Conrad
Blair Conrad

Reputation: 241714

This is not possible. FakeItEasy will only be able to intercept methods on fakes that it's created, and you're newing 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

Related Questions