Конструктор использует фиктивный объект, как мне протестировать метод изолированно? - PullRequest
4 голосов
/ 22 января 2011

У меня есть класс, который выглядит следующим образом:

class MyClass {

  private IDependency dep;

  public MyClass(IDependency dep) {
    this.dep = dep;
    this.dep.Reset();
  }

  public void Clear() {
    this.dep.Reset();
  }
}

Как проверить, что метод Reset правильно вызывается в методе Clear, игнорируя при этом то, что делает конструктор?

MyТест Moq выглядит следующим образом:

MockRepository mocks = new MockRepository(MockBehavior.Default);
var dep = mocks.Create<IDependency>();

dep.Setup(s => s.Reset());

MyClass myclass = new MyClass(dep.Object);
myclass.Clear():

state.Verify(s => s.Reset(), Times.Exactly(1));

Он не проходит, потому что Reset вызывался дважды (один раз в конструкторе и один раз в методе Clear).

Ответы [ 5 ]

2 голосов
/ 22 января 2011

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

Например, вы можете проверить, что ваш метод был вызван:

var mock = new Mock<IDependency>();
var subject = new MyClass(mock.Object);

subject.Clear();

mock.Verify( dep => dep.Reset(), Times.AtMost(2));

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

Тот факт, что ваш конструктор должен вызывать этот метод для зависимости, предполагает, что этот объект знает слишком много информации о деталях реализации зависимости. Это нарушает принцип Open Closed и закрывает вас от сценариев, когда вы не хотите, чтобы метод Reset вызывался при его инициализации.

Также учтите, что для любого класса или теста, использующего конкретный объект MyClass в качестве фиктивного параметра, потребуется инициализация Mock, иначе вы получите исключение NullReferenceException. Это добавляет значительную нагрузку на написание ваших тестов и добавляет уровень хрупкости, который приравнивается к долгосрочному обслуживанию и ложным негативам в ваших тестах. Единственный способ обойти это - сделать все интерфейсом, который хотя и эффективен, но и не является лучшей долгосрочной стратегией.

Согласно http://googletesting.blogspot.com/2009/07/separation-anxiety.html, использование Фабрики уменьшит некоторую часть этой связи и откроет вас для лучшего повторного использования этого объекта.

2 голосов
/ 22 января 2011

Я надеялся, что будет лучший способ сделать это, но макет будет записывать все вызовы на Reset, поэтому при использовании стандартного вызова Verify всегда будет возвращаться 2. Следующее поддерживает отдельный счетчик, который не очень элегантно. Если есть встроенный способ сделать это с Moq, я бы хотел знать.

int clearResetCount = 0;

Mock<IDependency> dep = new Mock<IDependency>();

MyClass myclass = new MyClass(dep.Object);

dep.Setup(s => s.Reset()).Callback(() => clearResetCount++);

Assert.AreEqual(0, clearResetCount, "Expected clear reset count - before.");

myclass.Clear();

Assert.AreEqual(1, clearResetCount, "Expected clear reset count - after.");
1 голос
/ 22 марта 2011

Я столкнулся с той же проблемой.

Выполните следующие действия, чтобы макет только записывал поведение метода Clear:

MockRepository mocks = new MockRepository(MockBehavior.Default);
var dep = mocks.Create<IDependency>();

MyClass myclass = new MyClass(dep.Object);

// setting up the mock just before calling the method under test
// will ignore any prior call to IDependency.Reset
int resetTimes = 0;
dep.Setup(s => s.Reset()).Callback(() => resetTimes++);

myclass.Clear();

mocks.VerifyAll();
Assert.That(resetTimes, Is.EqualTo(1));
0 голосов
/ 22 января 2011

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

class DependencySpy : IDependency {
    public int ResetCallCount { get; private set; }
    public void Reset() { ResetCallCount++; }
    public void ClearResetCallCount() { ResetCallCount = 0; }
}

Тест можно записать как

// Setup
var spy = new DependencySpy;
MyClass myclass = new MyClass(spy);
spy.ClearResetCallCount();
// Exercise
myclass.Clear();
// Validate
Assert.AreEqual(1, spy.ResetCallCount);
0 голосов
/ 22 января 2011

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...