简体   繁体   English

使用Action模拟方法<T>

[英]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. 我是Unit Testing的新手,很高兴知道我是否犯了错误或者没有朝着正确的方向前进。

Here is the situation: 情况如下:

I am trying to test a method ( MethodUnderTest ) which calls another method( MethodWithAction ) which takes Action<T> as argument. 我正在尝试测试一个方法( MethodUnderTest ),该方法调用另一个方法( MethodWithAction ),该方法将Action<T>作为参数。 I want to mock MethodWithAction , but test the logic based on the return value. 我想模拟MethodWithAction ,但是根据返回值测试逻辑。

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. 获取在回调中传递给mock的action参数,并使用示例字符串调用它。

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. 参考Moq Quickstart以更好地理解如何使用此模拟框架。

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): 我的第一直觉是重构ClassUnderTestIInterface以便IInterface有一个get属性,你可以完全删除IInterface实现的依赖,而MyClass只有一个工作要做(存储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. 因此,我们可以完全删除Action,并且在测试时代码更易读,更容易理解。

Just another way of looking at the problem. 只是看待问题的另一种方式。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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