2008-12-03 2 views
11

В настоящее время я начинаю вводить концепцию объектов Mock в свои модульные тесты. В частности, я использую структуру Moq. Тем не менее, одна из вещей, которые я заметил, это то, что внезапно классы, которые я тестирую с использованием этой структуры, отображают покрытие кода 0%.Как я могу использовать Mock Objects в своих модульных тестах и ​​по-прежнему использовать 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!"; 
     } 
    } 
} 

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

ответ

15

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

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"; 
} 

Теперь, если у вас есть эта линия в тесте:

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

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

+0

Отсутствует скобка в примере GetSomeString после параметра. – 2017-11-13 12:03:59

6

Большая ошибка - насмешка System Under Test (SUT), вы проверяете что-то еще. Вы должны высмеивать только зависимости SUT.

0

Это имеет большой смысл. По существу, вы говорите, что мне нужно делать следующее:

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

По существу экземпляра SUT и только с использованием издевается для классов SUT требует?

+0

Плюс ваш метод GetSomeString теперь будет иметь покрытие, потому что вы вызываете реальный GetSomeString, но только с поддельным объектом, который вам нужен, чтобы вы не шли глубже и не запускали какие-либо ракеты, о которых вы не знаете :) Все это касается тестирования в изоляции. – 2008-12-03 05:52:51

2

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

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

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

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

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

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

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

I.e. В каждом тесте зависимости тестируемого класса создаются специально для реализации определенной части кода.

Смежные вопросы