Ricardo Alves
Ricardo Alves

Reputation: 1121

C# Moq Intercept method call

I'm not sure if what I want to do is possible. I have an interface that looks like this:

public interface IObject
{
     void MethodA(ObjectA arg1);
     void MethodB(ObjectB arg1, ObjectC arg2);
     void MethodC(ObjectD arg1, ObjectE arg2);
}

I have somthing like the following implementation:

public class ObjectImplementation : IObject
{
     public void MethodA(ObjectA arg1)
     {
          if(arg1.Something)
          {
               MethodB(new ObjectB(arg1), new ObjectC(arg1));
          }
          else
          {
               MethodC(new ObjectD(arg1), new ObjectE(arg1));
          }
     }
}

I'm trying to write a unit test to test if those calls to methodB or methodC are being made according to the condition I have. How something like that can be achieved?

Upvotes: 5

Views: 3361

Answers (3)

Maxim Kosov
Maxim Kosov

Reputation: 1980

While accepted solution should work I would strongly advise you against such kind of tests. Each method call results in some output and some side effects. So, if there is no output and no side effects then method simply does nothing. By asserting which MethodB or MethodC was called you don't check MethodA's output neither it's side effects. Such tests are brittle as they test only one possible implementation of MethodA not what it actually does. By brittle I mean the following:

suppose you have following implementation of IObject

class ObjectImpl
{
    public void MethodA(ObjectA arg1)
    {
        if (arg1.Something)
        {
            MethodB(new ObjectB(arg1), new ObjectC(arg1));
        }
        else
        {
            MethodC(new ObjectD(arg1), new ObjectE(arg1));
        }
    }

    public void MethodB(ObjectB arg1, ObjectC arg2)
    {
        Console.WriteLine("Hi {0} and {1}", arg1, arg2);
    }

    public void MethodC(ObjectD arg1, ObjectE arg2)
    {
        Console.WriteLine("Bye {0} and {1}", arg1, arg2);
    }
}

So, essentially MethodA just prints "Hi" or "Bye" to the console. Then if somebody will notice that MethodB and MethodC won't used anywhere except MethodA he may just remove them. Notice that MethodA is still the same so nothing gets broken except test.

class ObjectImpl
{
    // Everything works as before except broken build
    public void MethodA(ObjectA arg1)
    {
        if (arg1.Something)
        {
            Console.WriteLine("Hi {0} and {1}", new ObjectB(arg1), new ObjectC(arg1));
        }
        else
        {
            Console.WriteLine("Bye {0} and {1}", new ObjectD(arg1), new ObjectE(arg1));
        }
    }
}

Upvotes: 0

Alpha75
Alpha75

Reputation: 2280

You are trying to mock an interface and verify an implementation of this interface.

You could make methods C and D virtual and use the implementation in the mock.

Implementation:

public class ObjectImplementation : IObject
{
    public void MethodA(ObjectA arg1)
    {
        if (arg1.Something)
        {
            MethodB(new ObjectB(arg1), new ObjectC(arg1));
        }
        else
        {
            MethodC(new ObjectD(arg1), new ObjectE(arg1));
        }
    }

    public virtual void MethodB(ObjectB arg1, ObjectC arg2)
    {

    }

    public virtual void MethodC(ObjectD arg1, ObjectE arg2)
    {

    }
}

Test:

[Fact]
public void Test_WhenSomethingIsTrue_MethodB_Invoked_WithObjects_B_And_C()
{
    // Arrange
    Mock<ObjectImplementation> mockObject = new Mock<ObjectImplementation>();
    ObjectA arg = new ObjectA();
    arg.Something = true;

    // Act
    mockObject.Object.MethodA(arg);

    // Assert
    mockObject.Verify(o => o.MethodB(It.Is<ObjectB>(b=> b.Arg == arg), It.Is<ObjectC>(c => c.Arg == arg)));
}

[Fact]
public void Test_WhenSomethingIsFalse_MethodC_Invoked_WithObjects_D_And_E()
{
    // Arrange
    Mock<ObjectImplementation> mockObject = new Mock<ObjectImplementation>();
    ObjectA arg = new ObjectA();
    arg.Something = false;

    // Act
    mockObject.Object.MethodA(arg);

    // Assert
    mockObject.Verify(o => o.MethodC(It.Is<ObjectD>(d => d.Arg == arg), It.Is<ObjectE>(e => e.Arg == arg)));
}

Upvotes: 6

Rahul
Rahul

Reputation: 77876

Well why can't you simply fake that method stub like

Mock<IObject> m = new Mock<IObject>();
m.Setup(x => s.MethodB(new ObjectB(arg1), new ObjectC(arg1)))
 .Callback(() => Console.WriteLine("MethodB Called"));

Thus if your condition if(arg1.Something) satisfies then the mocked one will be called and you will know

Upvotes: 0

Related Questions