简体   繁体   中英

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?

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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM