Как я могу проверить, вызывается ли закрытый метод класса с помощью rhino mock?

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

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

  public void calculateItems()
    {
        var result = new Result(fileName, ip, localPath, remotePath);

        calculateItems(result, nameOfString);
    }

    private void calculateItems(Result result, string nameOfString )

Как вы видите из приведенного выше кода, у меня есть два метода с абсолютно одинаковыми именами calcItems, но у общедоступного нет параметров, у частного - два параметра. Я пытаюсь понять, когда я вызываю public один в моем unittest, вызывается закрытый метод?

    private CalculateClass sut;
    private Result result; 

    [SetUp]
    public void Setup()
    {
      result = MockRepository.GenerateStub<Result>();
      sut = new CalculateClass();
    }

    [TearDown]
    public void TearDown()
    {

    }

    [Test]
    public void test()
    {     
        sut.Stub(stub => stub.calculateItems(Arg<Result>.Is.Anything, Arg<string>.Is.Anything));

        sut.calculateItems();

        sut.AssertWasCalled(stub => stub.calculateItems(Arg<Result>.Is.Anything, Arg<string>.Is.Anything));
    }

В моем unittest я принимаю такую ​​ошибку, которая гласит: "Метод перегрузки для convertItems не принимает два аргумента". Есть ли способ проверить это без каких-либо изменений в исходном коде?

3 ответа

Вы проверяете не ту вещь. Частные методы являются частными. Они не имеют отношения к потреблению кода, а модульные тесты потребляют код, как и любой другой.

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

Итак, вопрос, который вы должны задать себе... Каковы ожидаемые результаты при вызове этой операции?:

calculateItems()

Он ничего не возвращает, так что же он делает? В каком состоянии он каким-то образом изменяется? Это то, что ваш тест должен соблюдать, не детали реализации, а наблюдаемый результат. (И если операция не имеет наблюдаемого результата, то нет разницы между "пройдено" или "не удалось", поэтому проверять нечего.)

Мы не можем видеть детали вашего кода, но возможно, что наблюдаемый результат полностью связан с другим компонентом. Если это так, то другой компонент является зависимостью для этой операции, и цель модульного теста состоит в том, чтобы смоделировать эту зависимость, чтобы операция могла быть протестирована независимо от зависимости. Затем может потребоваться изменить компонент, чтобы обеспечить зависимость, а не внутреннее управление. (Это называется принципом обращения зависимостей.)


Также следует отметить...

но я не могу изменить исходный код

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

Имейте в виду, что программистам не только возможно, но, к сожалению, очень часто пишут код, который не может быть осмысленно проверен модулем. Если этот код был предоставлен вам кем-то другим, и вам запрещено изменять его по какой-либо нетехнической причине, то это будет обязанность другого лица исправить код. "Исправление" может включать в себя "обеспечение возможности значимого модульного тестирования". (Или, честно говоря, это должно быть модульное тестирование. Не вы.)

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

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

Теперь давайте обсудим, что вы на самом деле тестируете.

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

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

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

Когда вы говорите, что вам нужно знать, вызываются ли ваши частные методы, это может иметь две разные интерпретации:

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

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

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

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

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

И, в отличие от других мнений, вы должны проверить реализацию. Основная цель юнит-тестирования - найти ошибки в коде. Разные реализации имеют разные ошибки. Вот почему люди также используют инструменты покрытия, чтобы увидеть, охватили ли они код своей реализации. И, охват недостаточен, вам также необходимо проверить граничные случаи выражений и т. Д. Конечно, наличие ремонтопригодных тестов и тестов, которые не ломаются без необходимости в случае рефакторингов, являются хорошими целями (почему тестирование через открытый API, как правило, хороший подход - но не всегда), но они являются второстепенными целями по сравнению с целью найти все ошибки.

Другие вопросы по тегам