模拟测试方法

Posted

技术标签:

【中文标题】模拟测试方法【英文标题】:Mock a method for test 【发布时间】:2016-07-20 14:35:46 【问题描述】:

试图模拟在另一个方法中调用的方法。

// code part
public virtual bool hello(string name, int age)

    string lastName = GetLastName();


public virtual string GetLastName() 

    return "xxx"; 


// unit test part
Mock<Program> p = new Mock<Program>();
p.Setup(x => x.GetLastName()).Returns("qqq");

我希望 GetLastName 方法始终返回“qqq”。

【问题讨论】:

您尝试过的方法有什么问题?看起来正确 放断点的时候我姓氏的值还是=xxx。单元测试也失败了 如果你只想模拟在扩展 xmethod 类中调用的 base.xmethod() 该怎么办 【参考方案1】:

这应该可行,假设这些是完整的方法实现

public class MyProgram


    public bool hello(string name, int age)
    
        string lastName = GetLastName();

        return string.Format("hello 0", lastName);
    

    public virtual string GetLastName() 
    
        return "xxx"; 
    


public class MyProgramTests


    [TestMethod]
    public void MyTest()
    

        string stringToReturn = "qqq";
        Mock<MyProgram> name = new Mock<MyProgram>();
        name.CallBase = true;
        name.Setup(x => x.GetLastName()).Returns(stringToReturn );

        var results = name.Object.hello(It.IsAny<string>(), It.IsAny<int>());

        string expected = string.Format("hello 0", results);

        Assert.AreEqual(expected, results);
    

我还是不太听从你的评论:

Mock 参数的真正含义是什么?应该是什么?是?对不起,我不太明白语法。为了澄清,模拟意味着当我在我的代码中放置断点时,断点应该跳过我正在模拟的方法。我说的对吗?

Mock&lt;T&gt; 允许您模拟T - T 的类型作为通用指标,也意味着几乎任何类的东西。在传统中,您将模拟 interface,而不是实际的 class,但在上面的示例中,我们正在模拟一个类。对于贴出的样例单元测试,单元测试的目的是测试hello(string, int)的实现。我们知道hello(string, int) 依赖于该类中称为GetLastName() 的另一个方法。 GetLastName()s 的实现虽然重要,但对于单元测试 hello(string, int) 的范围并不重要。出于这个原因,我们模拟了调用及其返回 - 以便测试 hello(string, int) 的功能,而不必担心其依赖项的实现。

我用实际的类名包围了上面的内容,希望能更明显地表明我们正在模拟 MyProgram 类并提供 GetLastName() 的新实现(模拟)

感谢您的回答。如果我想测试一个调用另一个方法的方法又调用另一个方法怎么办?例如。如果方法 hello 调用了另一个方法呢?

同样的原则适用于构建单元测试时(假设它们是单元测试,而不是集成测试或其他,您总是希望专注于测试一个公共方法。What's the difference between unit and integration tests?

public class Foo


    public string Bar()
    
        return string.Format("0Bar", Baz(5));;
    

    public virtual string Baz(int someNumber)
    
        return string.Format("0Baz", DoStuff(someNumber).ToString());
    

    public virtual int DoStuff(int someNumber)
    
        return someNumber+1;
    


如果我们对Bar() 进行单元测试,我们将不关心Baz(int) 或更糟糕的DoStuff(int) 的实现。请注意我们不关心实现,我们确实关心它们的返回值。从Bar()s 的角度来看,唯一重要的是Baz(int) 返回一个字符串。什么字符串? Bar()s 单元测试没关系。

Bar() 的示例测试:

[TestMethod]
public void Bar_ReturnsBazValueWithBarAppended

    // Arrange
    string testBazReturn = "test";
    Mock<Foo> mock = new Mock<Foo>();
    mock.CallBase = true;
    mock
        .Setup(s => s.Baz(It.IsAny<int>())
        .Returns(testBazReturn);

    // Act
    var results = mock.Object.Bar();

    // Assert
    Assert.AreEqual(string.Format("01", testBazReturn, "Bar"), results);
    mock.Verify(v => v.Baz(It.IsAny<int>())); // Verifies that Baz was called

请注意,在上面,我们对Baz(int)DoStuff(int) 的实际实现并不重要,因为我们忽略了Baz(int)DoStuff(int) 的实际实现,甚至没有发挥作用。

现在,如果我们要测试Baz(int),我们只需遵循相同的心态:

[TestMethod]
public void Baz_ReturnsDoStuffValueWithBazAppended

    // Arrange
    int testDoStuffReturn = 1;
    Mock<Foo> mock = new Mock<Foo>();
    mock.CallBase = true;
    mock
        .Setup(s => s.DoStuff(It.IsAny<int>())
        .Returns(testDoStuffReturn);

    // Act
    var results = mock.Object.Baz(5);

    // Assert
    Assert.AreEqual(string.Format("01", results, "Baz"), results); // Validates the result
    mock.Verify(v => v.DoStuff(It.IsAny<int>())); // Verifies that DoStuff was called

在上面,既然我们正在对Baz(int)进行单元测试,我们不关心Bar(),而我们在DoStuff(int)中唯一关心的是它返回一个值(但不是如何达到这个值。)

最后是DoStuff(int)

[TestMethod]
public void DoStuff_ReturnsParameterPlusOne()

    // Arrange
    Foo foo = new Foo();
    int passed = 1;
    int expected = passed + 1;

    // Act
    var results = foo.DoStuff(passed);

    // Assert
    Assert.AreEqual(expected, results);

【讨论】:

我应该使用基类还是扩展类,或者这对程序无关紧要? (Mock 中的参数) 我不明白你的问题,你能用你的意思的例子详细说明问题吗? Mock 参数的真正含义是什么?> 应该是什么?是?对不起,我不太明白语法。为了澄清,模拟意味着当我在我的代码中放置断点时,断点应该跳过我正在模拟的方法。我说的对吗? 感谢您的回答。如果我想测试一个调用另一个方法的方法又调用另一个方法怎么办?例如。如果方法 hello 调用了另一个方法呢? 通过使用 virtual 只是为了模拟一种方法,您正在采用危险的代码气味。在这里使用接口会更安全。

以上是关于模拟测试方法的主要内容,如果未能解决你的问题,请参考以下文章

模拟测试方法

将模拟类注入方法以单元测试方法

来自与测试方法使用的同一类的模拟方法

模拟文件数据以测试表单的正确方法是啥?

Nestjs 单元测试 - 模拟方法守卫

使用PowerMockito和Mockito进行模拟测试,包括静态方法测试,私有方法测试等