Как я могу использовать фиктивные объекты в своих модульных тестах и ​​по-прежнему использовать покрытие кода?

В настоящее время я начинаю вводить концепцию Mock-объектов в свои модульные тесты. В частности, я использую фреймворк Moq. Однако одна из вещей, которые я заметил, заключается в том, что классы, которые я тестирую с помощью этого фреймворка, внезапно показывают покрытие кода 0%.

Теперь я понимаю, что, поскольку я просто издеваюсь над классом, он не запускает сам класс .... но как мне написать эти тесты и получить точные результаты с помощью Code Coverage? Нужно ли мне писать один набор тестов, использующих Mocks, и один набор для непосредственного создания экземпляра класса.

Возможно, я делаю что-то не так, не осознавая этого?

Вот пример того, как я пытаюсь протестировать класс под названием «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!";
        }
    }
}

Кто-нибудь знает, что мне делать по другому?


person mezoid    schedule 03.12.2008    source источник


Ответы (3)


Вы неправильно используете свои фиктивные объекты. Когда вы используете фиктивные объекты, вы должны тестировать, как ваш код взаимодействует с другими объектами, не используя на самом деле настоящие объекты. См. Код ниже:

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();
        }
    }

Не похоже, что он делает что-то полезное, когда ваш код просто возвращает строку без какой-либо логики.

Настоящая сила приходит, если вы GetSomeString() метод выполнили некоторую логику, которая может изменить результат выходной строки в зависимости от возврата от метода IDependdance .GiveMeAString(), тогда вы можете увидеть, как ваш метод обрабатывает неверные данные, отправляемые из интерфейса IDependdance.

Что-то типа:

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

Теперь, если у вас есть эта строка в вашем тесте:

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

Что будет с вашим GetSomeString() методом?

person Nathan W    schedule 03.12.2008
comment
В вашем примере GetSomeString после параметра отсутствует скобка. - person Adam Short; 13.11.2017

Большая ошибка - издеваться над тестируемой вами тестируемой системой (SUT) что-то другое. Вы должны имитировать только зависимости SUT.

person Aleš Roubíček    schedule 03.12.2008

Я бы рекомендовал держаться подальше от имитации фреймворков, пока вы не поймете, какие взаимодействия здесь происходят.

ИМО, лучше учиться с помощью созданных вручную тестовых двойников, а затем переходить на имитацию фреймворка. Мои рассуждения:

  1. Мокирующие фреймворки абстрагируются от того, что на самом деле происходит; легче понять взаимодействия, если вам нужно явно создать свои зависимости, а затем следовать тестам в отладчике.

  2. Фреймворки легко использовать неправильно. Если вы будете кататься самостоятельно, когда учитесь, вы с большей вероятностью поймете разницу между разными типами двойных тестов. Если вы сразу перейдете к фреймворку mock, вы легко сможете использовать mock, когда вам нужны заглушки, и наоборот - есть большая разница.

Подумайте об этом так: в центре внимания находится тестируемый класс. Вы создаете его экземпляр, вызываете его методы, а затем утверждаете, что результат правильный. Если тестируемый класс имеет зависимости (например, что-то требуется в конструкторе), вы удовлетворяете эти зависимости, используя либо A: реальные классы, либо B: тестовые двойники.

Причина, по которой мы используем тестовые двойники, заключается в том, что он изолирует тестируемый класс, а это означает, что вы можете выполнять его код более контролируемым образом.

Например. если у вас есть класс, содержащий сетевой объект, вы не можете протестировать процедуры обработки ошибок класса-владельца, которые обнаруживают мертвые соединения, если вы вынуждены использовать конкретный объект сетевого соединения. Вместо этого вы вставляете фальшивый объект подключения и приказываете ему генерировать исключение при вызове его метода «SendBytes».

Т.е. В каждом тесте зависимости тестируемого класса создаются специально для выполнения определенного фрагмента кода.

person Mark Simpson    schedule 06.06.2009