9

I have an interface:

public interface IRepeater
{
    void Each(string path, Action<string> action);
}

I want to mock this interface using Moq. Now I can obviously do the following:

var mock = new Mock<IRepeater>();
mock.Setup(m => m.Each(It.IsAny<string>(), It.IsAny<Action<string>>());

However, to aid testing I want to be able to mock the string that gets passed to the Action<string>. Can this be done with Moq? If yes, how?

Update

To clarify I am testing a different class that has a dependency on IRepeater. I want to mock IRepeater.Each so I can control the string that the Action gets so I can test the behaviour.

Example

So if I have a class like so.

public class Service
{
    private readonly IRepeater _repeater;

    public Service(IRepeater repeater)
    {
        _repeater = repeater;
    }

    public string Parse(string path)
    {
        var builder = new StringBuilder();

        _repeater.Each(path, line => builder.Append(line));

        return builder.ToString();
    }
}

How do I mock IRepeater.Each so that I can test Service.Parse?

Old Fox
  • 8,629
  • 4
  • 34
  • 52
baynezy
  • 6,493
  • 10
  • 48
  • 73
  • Your mock is an implementation of `IRepeater`, `Each` will do absolutely nothing (at the moment), regardless of what's passed in. When you're writing the mocked implementation of `Each` then you're able to do whatever you want with the action - including passing whatever string you'd like. – Rob Dec 20 '15 at 07:46
  • To rephrase it, the *actual* implementation of `IRepeater` is responsible for passing the string to the action. However, you've completely mocked that part away. Could you please give an example of what you're trying to achieve? – Rob Dec 20 '15 at 07:49
  • 1
    @Rob - I have tried to make it more clear – baynezy Dec 20 '15 at 13:58

2 Answers2

11

You have to use callback method. Since line => builder.Append(line) is part of the method behavior, you have to execute this behavior when you test the method:

    [TestMethod]
    public void Test_Service_When_Passing_String_And_ActionDelegate()
    {
        var fakeReporter = new Mock<IRepeater>();

        fakeReporter.Setup(x => x.Each(It.IsAny<string>(), It.IsAny<Action<string>>()))
            .Callback<string, Action<string>>((s, action) => action(s));

        var target = new Service(fakeReporter.Object);

        var result = target.Parse("asdfghj");

        Assert.AreEqual("asdfghj", result);
    }

Another approach to test this method is to verify the method was called with the correct path and then verify that the action is the correct action:

     [TestMethod]
    public void Test_Service_When_Passing_String_And_ActionDelegate()
    {
        var fakeReporter = new Mock<IRepeater>();

        fakeReporter.Setup(x => x.Each(It.IsAny<string>(), It.IsAny<Action<string>>()))
            .Callback<string, Action<string>>((s, action) =>
            {
                Assert.AreEqual("asdfghj", s);
                foreach (var w in "pass")
                {
                    action(w.ToString());
                }
            });

        var target = new Service(fakeReporter.Object);

        var result = target.Parse("asdfghj");

        Assert.AreEqual("pass", result);
    }

BTW you can replace the It.IsAny<string>() with the string and then remove the Assert.AreEqual("asdfghj", s);(I just like to test things in the explicit way...)

Old Fox
  • 8,629
  • 4
  • 34
  • 52
0

Seems like you are looking to verify that a passed Action (delegate) will be passed to the IRepeater Call. Because you are not testing the Repeater but a Repeater caller (The Repeater is the mock and not the tested subject).

Here is how I would have done it:

public class Service
{
    private readonly IRepeater repeater;

    public Service(IRepeater repeater)
    {
        this.repeater = repeater;
    }

    public void Foo(string str, Action<string> action)
    {
        repeater.Each(str, action);
    }
}

public class ActionImplement
{
    public virtual void Action(string str)
    {
        Console.Write(str);
    }
}

public interface IRepeater
{
    void Each(string path, Action<string> action);
}

And the test would have verify the passing of ActionImplement.Action

    [TestMethod]
    public void Test_Service_When_Passing_String_And_ActionDelegate()
    {
        var actionImplement = new Mock<ActionImplement>();
        actionImplement.Setup(m => m.Action(It.IsAny<string>()));

        var mock = new Mock<IRepeater>();
        mock.Setup(m => m.Each(It.IsAny<string>(), actionImplement.Object.Action));


        var srv = new Service(mock.Object);
        srv.Foo("aa",actionImplement.Object.Action);


        mock.Verify(ai => ai.Each("aa", actionImplement.Object.Action));


    }
Amir Katz
  • 1,027
  • 1
  • 10
  • 24
  • thanks. I don't think I was quite clear enough. So I have updated my question. – baynezy Dec 20 '15 at 13:59
  • Hi baynezy, Seems like you are still not trying to mock a return value since it will return void. You are actually trying to verify a call to Each with a certain delegate. – Amir Katz Dec 20 '15 at 14:49
  • That's right I'm trying to control the behaviour so I can test the method under test. – baynezy Dec 20 '15 at 17:15
  • 1
    @AmirKatz Since `line => builder.Append(line)` is part of the method under test behavior he needs to verify that `line => builder.Append(line)` was pass... the way to do it is through `callback` method. – Old Fox Dec 20 '15 at 18:17