Как смоделировать метод, который также принадлежит самому целевому классу? - PullRequest
7 голосов
/ 11 октября 2010

Допустим, мы тестируем класс C, который имеет 2 метода M1 и M2, где M1 вызывает M2 при выполнении.

Тестирование M2 в порядке, но как мы можем проверить M1? Сложность в том, что нам нужно издеваться над М2, если я не неправильно понимаю вещи.

Если это так, как мы можем издеваться над другим методом при тестировании метода, определенного в том же классе?

[Изменить]
Класс C не имеет базовых классов.

Ответы [ 3 ]

7 голосов
/ 11 октября 2010

Вы можете сделать это, установив свойство CallBase на макете в true.

Например, если у меня есть этот класс:

public class Foo
{
    public virtual string MethodA()
    {
        return "A";
    }
    public virtual string MethodB()
    {
        return MethodA() + "B";
    }
}

Я могу настроить MethodA и вызвать MethodB:

[Fact]
public void RunTest()
{
    Mock<Foo> mockFoo = new Mock<Foo>();
    mockFoo.Setup(x => x.MethodA()).Returns("Mock");
    mockFoo.CallBase = true;

    string result = mockFoo.Object.MethodB();

    Assert.Equal("MockB", result);
}
6 голосов
/ 11 октября 2010

Вы должны позволить вызову M1 пройти к реальному экземпляру метода M2.

В общем, вы должны проверять поведение черного ящика вашегоклассы.Ваши тесты не должны заботиться о том, чтобы M1 вызывал M2 - это деталь реализации.

Это не то же самое, что насмешливые внешние зависимости (что вы должны сделать) ...


Например, скажем, у меня есть такой класс:

class AccountMerger
{
    public AccountMerger(IAccountDao dao) 
    {
        this.dao = dao;
    }     

    public void Merge(Account first, Account second, MergeStrategy strategy) 
    {
        // merge logic goes here...
        // [...]
        dao.Save(first);
        dao.Save(second);
    }

    public void Merge(Account first, Account second) 
    {
        Merge(first, second, MergeStrategy.Default);
    }

    private readonly IAccountDao dao;
}

Я хочу, чтобы мои тесты показали, что:

  1. Вызов Merge(first, second, strategy) приводит к сохранению двух учетных записей, которые были объединены с использованием предоставленного правила.

  2. Вызов Merge(first, second) приводит к сохранению двух учетных записей, которые были объединены с использованием правила по умолчанию.

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

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

    // ...
    // refactored AccountMerger methods
    // these still work, and still fulfil the two requirements above

    public void Merge(Account first, Account second, MergeStrategy strategy) 
    {
        MergeAndSave(first, second, strategy ?? MergeStrategy.Default);
    }

    public void Merge(Account first, Account second) 
    {
        // note this no longer calls the other Merge() method
        MergeAndSave(first, second, MergeStrategy.Default);
    }

    private void MergeAndSave(Account first, Account second, MergeStrategy strategy) 
    {
        // merge logic goes here...
        // [...]
        dao.Save(first);
        dao.Save(second);
    }

    // ...

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

С другой стороны, я имею дело с AccountMerger с использованием IAccountDao для сохранения учетных записей послеслияние (хотя AccountMerger не должно заботиться о реализации DAO, только то, что у него есть метод Save().) Этот DAO является главным кандидатом для насмешек - external зависимость AccountMerger класс, чувствуя эффект Я хочу проверить входные данные .

4 голосов
/ 12 октября 2010

Вы не должны насмехаться над методами в целевом классе, вы должны только издеваться над внешними зависимостями.

Если кажется, что имеет смысл издеваться над M2 во время тестирования M1, это часто означает, что ваш класс делает слишком много вещей.Подумайте о разделении класса и сохранении M2 в одном классе и переместите M1 в класс более высокого уровня, который будет использовать класс, содержащий M2.Тогда издеваться над M2 легко, и ваш код станет лучше.

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