Mock a method for test

前端 未结 1 1864
栀梦
栀梦 2021-02-19 19:44

Trying to mock a method that is called within another method.

// code part
public virtual bool hello(string name, int age)
{
    string lastName = GetLastName();
         


        
1条回答
  •  情歌与酒
    2021-02-19 20:08

    This should work, assuming those are the full method implementations

    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 name = new Mock();
            name.CallBase = true;
            name.Setup(x => x.GetLastName()).Returns(stringToReturn );
    
            var results = name.Object.hello(It.IsAny(), It.IsAny());
    
            string expected = string.Format("hello {0}", results);
    
            Assert.AreEqual(expected, results);
        }
    }
    

    I'm still not quite following your comment:

    What does the parameter really mean of Mock What should ?? be? excuse me, I don't quite understand the syntax. To clarify, mock means that when I put break points in my code I the breakpoints should skip the methods that I am mocking. Am I right?

    Mock allows you to mock a type of T - T being a generic indicator, also meaning pretty much anything that's a class. In the traditional, you would be mocking an interface, not an actual class, but in the example above, we're mocking a class. For the sample unit test posted, the purpose of the unit test is to test the implementation of hello(string, int). We know that hello(string, int) relies on another method within that class called GetLastName(). GetLastName()s implementation, while important, is not important to the scope of unit testing hello(string, int). For this reason, we mock the call and its return - in order to test the functionality of hello(string, int) without having to worry about its dependency's implementation.

    I have surrounded the above with actual class names to hopefully make it more obvious that we're mocking the class MyProgram and providing a new implementation (mock) of GetLastName()

    Thanks for the answer. What if I want to test a method that calls another method that calls another method? For eg. what if the method hello called another method?

    The same principle applies, when you're building your unit tests (assuming they are unit tests, and not integration tests or other, you always want to concentrate on testing one public method. What's the difference between unit and integration tests?

    public class Foo
    {
    
        public string Bar()
        {
            return string.Format("{0}Bar", Baz(5));;
        }
    
        public virtual string Baz(int someNumber)
        {
            return string.Format("{0}Baz", DoStuff(someNumber).ToString());
        }
    
        public virtual int DoStuff(int someNumber)
        {
            return someNumber+1;
        }
    
    }
    

    If we're unit testing Bar() we do not care about the implementation of Baz(int) or even worse DoStuff(int). Note we don't care about the implementation, we do care that they return values. From Bar()s perspective, the only thing that is important is the Baz(int) returns a string. What string? It doesn't matter to Bar()s unit test.

    Sample test for Bar():

    [TestMethod]
    public void Bar_ReturnsBazValueWithBarAppended
    {
        // Arrange
        string testBazReturn = "test";
        Mock mock = new Mock();
        mock.CallBase = true;
        mock
            .Setup(s => s.Baz(It.IsAny())
            .Returns(testBazReturn);
    
        // Act
        var results = mock.Object.Bar();
    
        // Assert
        Assert.AreEqual(string.Format("{0}{1}", testBazReturn, "Bar"), results);
        mock.Verify(v => v.Baz(It.IsAny())); // Verifies that Baz was called
    }
    

    Notice in the above, our actual implementations of Baz(int), and DoStuff(int) do not matter, as we are disregarding the actual implementation of Baz(int), and DoStuff(int) doesn't even come into play.

    Now, if we were to test Baz(int) we just follow the same mentality:

    [TestMethod]
    public void Baz_ReturnsDoStuffValueWithBazAppended
    {
        // Arrange
        int testDoStuffReturn = 1;
        Mock mock = new Mock();
        mock.CallBase = true;
        mock
            .Setup(s => s.DoStuff(It.IsAny())
            .Returns(testDoStuffReturn);
    
        // Act
        var results = mock.Object.Baz(5);
    
        // Assert
        Assert.AreEqual(string.Format("{0}{1}", results, "Baz"), results); // Validates the result
        mock.Verify(v => v.DoStuff(It.IsAny())); // Verifies that DoStuff was called
    }
    

    In the above, now that we're unit testing Baz(int), we don't care about Bar(), and the only thing we care about in DoStuff(int) is that it returns a value (but not how it arrives at that value.)

    And finally 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);
    }
    

    0 讨论(0)
提交回复
热议问题