Anjesh Aggarwal
Anjesh Aggarwal

Reputation: 53

Mocking a method with Action<T>

I am new with Unit Testing, would be happy to know if I am making any mistake or not proceeding the right direction.

Here is the situation:

I am trying to test a method (MethodUnderTest) which calls another method(MethodWithAction) which takes Action<T> as argument. I want to mock MethodWithAction, but test the logic based on the return value.

Here is the structure:

interface IInterface
{
    void MethodWithAction(Action<string> action);
}

class MyClass : IInterface
{
    public void MethodWithAction(Action<string> action)
    {
        string sampleString = "Hello there";
        action(sampleString);
    }
}

class ClassUnderTest
{
    public IInterface Obj = new MyClass();
    public string MethodUnderTest()
    {
        string stringToBeTested = string.Empty;

        Obj.MethodWithAction(str =>
        {
            if (str.Contains("."))
                stringToBeTested = string.Empty;
            else
                stringToBeTested = str.Replace(" ", string.Empty);
        });
        return stringToBeTested;
    }
}

My test method goes like this:

[TestMethod]
[DataRow("Hello, World", "Hello,World")]
[DataRow("Hello, World.","")]
[DataRow("Hello", "Hello")]
public void MethodUnderTestReturnsCorrectString(string sampleString, string expected)
{
    var mockObj = new Mock<IInterface>();
    mockObj.Setup(m=>m.MethodWithAction(It.IsAny<Action<string>>))
    .Callback(???);
    ClassUnderTest sut = new ClassUnderTest();
    sut.Obj=mockObj.Object;
    string actual = sut.MethodUnderTest();
    Assert.Equal(expected, actual);
 }

I would like to know what goes at the place of ??? in the test, or is there entirely different approach for this problem?

Upvotes: 3

Views: 3627

Answers (2)

LewisM
LewisM

Reputation: 1099

My first instinct would be to refactor ClassUnderTest and IInterface so that IInterface has a get property such that you remove the dependency of the IInterface implementation entirely and MyClass has only the one job to do (store the SampleString):

interface IInterface
{
    string SampleString { get; }
}

// Fix MyClass
class MyClass : IInterface
{
    public string SampleString => "Hello There"
}

class ClassUnderTest
{
    public string MethodUnderTest(IInterface someObject)
    {
        string stringToBeTested = string.Empty;

        if (someObject.SampleString.Contains("."))
            stringToBeTested = string.Empty;
        else
            stringToBeTested = str.Replace(" ", string.Empty);

        return stringToBeTested;
    }
}

So we can remove the Action altogether and the code is somewhat more readable and easier to follow when testing.

Just another way of looking at the problem.

Upvotes: 0

Nkosi
Nkosi

Reputation: 247018

Grab the action parameter that was passed to the mock in the call back and invoke it with the sample string.

mockObj
    .Setup(m => m.MethodWithAction(It.IsAny<Action<string>>))
    .Callback((Action<string> action) => action(sampleString));

Reference Moq Quickstart to get a better understanding of how to use this mocking framework.

Upvotes: 6

Related Questions