How can I use Mock Objects in my unit tests and st

2019-03-18 01:50发布

Presently I'm starting to introduce the concept of Mock objects into my Unit Tests. In particular I'm using the Moq framework. However, one of the things I've noticed is that suddenly the classes I'm testing using this framework are showing code coverage of 0%.

Now I understand that since I'm just mocking the class, its not running the actual class itself....but how do I write these tests and have Code Coverage return accurate results? Do I have to write one set of tests that use Mocks and one set to instantiate the class directly.

Perhaps I am doing something wrong without realizing it?

Here is an example of me trying to Unit Test a class called "MyClass":

using Moq;
using NUnitFramework;

namespace MyNameSpace
{
    [TestFixture]
    public class MyClassTests
    {

        [Test]
        public void TestGetSomeString()
        {
            const string EXPECTED_STRING = "Some String!";

            Mock<MyClass> myMock = new Mock<MyClass>();
            myMock.Expect(m => m.GetSomeString()).Returns(EXPECTED_STRING);

            string someString = myMock.Object.GetSomeString();

            Assert.AreEqual(EXPECTED_STRING, someString);
            myMock.VerifyAll();

        }

    }

    public class MyClass
    {
        public virtual string GetSomeString()
        {
            return "Hello World!";
        }
    }
}

Does anyone know what I should be doing differently?

4条回答
够拽才男人
2楼-- · 2019-03-18 02:05

Big mistake is mocking the System Under Test (SUT), you test something else. You should mock only SUT dependencies.

查看更多
再贱就再见
3楼-- · 2019-03-18 02:08

You are not using your mock objects correctly. When you are using mock objects you meant to be testing how your code interacts with other objects without actually using the real objects. See the code below:

using Moq;
using NUnitFramework;

namespace MyNameSpace
    {
        [TestFixture]
        public class MyClassTests
        {

            [Test]
            public void TestGetSomeString()
            {
                const string EXPECTED_STRING = "Some String!";

                Mock<IDependance> myMock = new Mock<IDependance>();
                myMock.Expect(m => m.GiveMeAString()).Returns("Hello World");

                MyClass myobject = new MyClass();

                string someString = myobject.GetSomeString(myMock.Object);

                Assert.AreEqual(EXPECTED_STRING, someString);
                myMock.VerifyAll();

            }

        }

        public class MyClass
        {

            public virtual string GetSomeString(IDependance objectThatITalkTo)
            {
                return objectThatITalkTo.GiveMeAString();
            }
        }

        public interface IDependance
        {
            string GiveMeAString();
        }
    }

It doesn't look like it is doing anything useful when your code is just returning a string without any logic behind it.

The real power comes if you GetSomeString() method did some logic that may change the result of the output string depending on the return from the IDependdance .GiveMeAString() method, then you can see how your method handles bad data being sent from the IDependdance interface.

Something like:

 public virtual string GetSomeString(IDependance objectThatITalkTo {
     if (objectThatITalkTo.GiveMeAString() == "Hello World")
     return "Hi";
 }

Now if you have this line in your test:

myMock.Expect(m => m.GiveMeAString()).Returns(null);

What will happen to your GetSomeString() method?

查看更多
冷血范
4楼-- · 2019-03-18 02:19

That makes a lot of sense. Essentially you're saying that I need to be doing the following:

public class MyClass
{
    public virtual string GetSomeString(MyOtherClass moc)
    {
        return moc.ToString();
    }
}

.....

Mock<MyOtherClass> myMock = new Mock<MyOtherClass>();

MyClass mc = new MyClass();

string someString = mc.GetSomeString(myMock.Object);
Assert.AreEqual(EXPECTED_STRING, someString);

Essentially instantiating the SUT and only using mocks for the classes the SUT requires?

查看更多
爱情/是我丢掉的垃圾
5楼-- · 2019-03-18 02:28

I would recommend staying away from mocking frameworks until you understand the interactions that are going on here.

IMO it's better to learn with manually created test doubles, then graduate to a mocking framework afterwards. My reasoning:

  1. Mocking frameworks abstract away what's actually happening; it's easier to grasp the interactions if you have to create your dependencies explicitly, then follow the tests in the debugger.

  2. It's easy to misuse frameworks. If you roll your own when you're learning, you are more likely to understand the differences between different type of test doubles. If you go straight to a mocking framework, it's easy to use mocks when you wanted stubs and vice versa -- there is a big difference.

Think of it this way: The class under test is the focus. You create an instance of it, call its methods and then assert that the result is correct. If the class under test has dependencies (e.g. something is required in the constructor), you satisfy those dependencies using either A: real classes or B: test doubles.

The reason we use test doubles is that it isolates the class under test, meaning that you can exercise its code in a more controlled fashion.

E.g. if you have a class that contains a network object, you cannot test the owning class's error handling routines that detect dead connections if you're forced to use a concrete network connection object. Instead, you inject a fake connection object and tell it to throw an exception when its "SendBytes" method is called.

I.e. In each test, the dependencies of the class under test are created specifically to exercise a particular piece of code.

查看更多
登录 后发表回答