Как вы тестируете частные методы с помощью NUnit?

Мне интересно, как правильно использовать NUnit. Сначала я создал отдельный тестовый проект, который использует мой основной проект в качестве ссылки. Но в этом случае я не могу проверять частные методы. Я предполагал, что мне нужно включить мой тестовый код в мой основной код?! - Это не похоже на правильный способ сделать это. (Мне не нравится идея отправки кода с тестами в нем.)

Как вы проверяете частные методы с помощью NUnit?

Ответ 1

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

Итак, NUnit не предоставляет никакого механизма для проверки непубличных членов.

Ответ 2

Общим шаблоном для написания модульных тестов является проверка только общедоступных методов.

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

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

Возможно, правильно переместить их в класс-помощник и сделать их общедоступными. Этот класс может не отображаться вашим API.

Этот тестовый код никогда не смешивается с вашим общедоступным кодом.

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

Ответ 3

Хотя я согласен с тем, что основное внимание в модульном тестировании должно быть публичным интерфейсом, вы получаете гораздо более подробное впечатление от своего кода, если вы также тестируете частные методы. Структура тестирования MS позволяет это с помощью PrivateObject и PrivateType, NUnit этого не делает. Вместо этого я делаю следующее:

private MethodInfo GetMethod(string methodName)
{
    if (string.IsNullOrWhiteSpace(methodName))
        Assert.Fail("methodName cannot be null or whitespace");

    var method = this.objectUnderTest.GetType()
        .GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance);

    if (method == null)
        Assert.Fail(string.Format("{0} method not found", methodName));

    return method;
}

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

Ответ 4

Можно проверить личные методы, объявив тестовую сборку в качестве сборки друга целевой сборки, которую вы тестируете. Подробнее см. Ссылку ниже:

http://msdn.microsoft.com/en-us/library/0tke9fxk.aspx

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

Как уже было сказано, вам действительно не нужно проверять частные методы. Вы больше, чем хотели бы реорганизовать свой код на более мелкие строительные блоки. Один совет, который может помочь вам, когда вы приходите в рефакторинг, - это попытаться подумать о домене, к которому относится ваша система, и подумать о "реальных" объектах, которые населяют этот домен. Ваши объекты/классы в вашей системе должны относиться непосредственно к реальному объекту, который позволит вам изолировать точное поведение, которое должен содержать объект, а также ограничить обязанности объектов. Это будет означать, что вы рефакторинг логически, а не просто для проверки конкретного метода; вы сможете проверить поведение объектов.

Если вы все еще чувствуете потребность протестировать внутреннее, вы также можете захотеть посмеяться в своем тестировании, так как вы хотите сосредоточиться на одном фрагменте кода. Mocking - это то, где вы вставляете в него вложения объектов, но вводимые объекты не являются "реальными" или производственными объектами. Они являются фиктивными объектами с жестко настроенным поведением, чтобы упростить выделение поведенческих ошибок. Rhino.Mocks - это популярная бесплатная фальшивая инфраструктура, которая, по сути, будет писать объекты для вас. TypeMock.NET(коммерческий продукт с доступной версией сообщества) - более мощная среда, которая может издеваться над объектами CLR. Очень полезно для издевательств над классами SqlConnection/SqlCommand и Datatable, например, при тестировании приложения базы данных.

Надеюсь, этот ответ даст вам немного больше информации, чтобы сообщить вам об модульном тестировании в целом и помочь вам получить лучшие результаты от Unit Testing.

Ответ 5

Этот вопрос в свои передовые годы, но я думал, что поделюсь этим путем.

В принципе, у меня есть все мои классы unit test в сборке, которую они тестируют в пространстве имен "UnitTest" ниже "по умолчанию" для этой сборки - каждый тестовый файл завернут в:

#if DEBUG

...test code...

#endif

и все это означает, что a) он не распространяется в релизе, и b) я могу использовать объявления уровня internal/Friend без перекоса.

Другое, что предлагает, более подходящее для этого вопроса, - это использование классов partial, которые могут быть использованы для создания прокси для тестирования частных методов, например, для проверки чего-то вроде частного метода, который возвращает целочисленное значение:

public partial class TheClassBeingTested
{
    private int TheMethodToBeTested() { return -1; }
}

в основных классах сборки и тестовом классе:

#if DEBUG

using NUnit.Framework;

public partial class TheClassBeingTested
{
    internal int NUnit_TheMethodToBeTested()
    {
        return TheMethodToBeTested();
    }
}

[TestFixture]
public class ClassTests
{
    [Test]
    public void TestMethod()
    {
        var tc = new TheClassBeingTested();
        Assert.That(tc.NUnit_TheMethodToBeTested(), Is.EqualTo(-1));
    }
}

#endif

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

Ответ 6

Основная цель модульного тестирования - проверить общедоступные методы класса. Эти общедоступные методы будут использовать эти частные методы. Тестирование модуля будет проверять поведение общедоступного.

Ответ 7

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

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

Ответ 8

Извините, если это не отвечает на вопрос, но решения, такие как использование рефлексии, #if #endif заявления или использование частных методов, не решают проблему. Могут быть несколько причин не делать видимыми частные методы... что, если производственный код и команда ретроспективно записывают модульные тесты, например.

Для проекта, над которым я работаю только с MSTest (к сожалению), есть способ, используя accessors, для unit test частных методов.

Ответ 9

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

Вы просто не должны тестировать ничего, что не публично.

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

Если ваш клиент может запустить Test-Suite и убедиться, что код, который вы доставили, фактически "работает", я не вижу в этом проблемы (если вы не передадите свой IP-адрес через этот). Вещи, которые я включаю в каждый выпуск, являются отчетами об испытаниях и отчетами о покрытии кода.

Ответ 10

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

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

Я парень по-парному, поэтому пакет visiblilty можно назвать чем-то совершенно другим в С#. Достаточно сказать, что это, когда два класса должны находиться в одном пространстве имен, чтобы получить доступ к этим методам.

Ответ 11

Вы можете защитить свои методы от внутренних, а затем использовать assembly: InternalVisibleTo("NAMESPACE")  к вашему пространству имен для тестирования.

Следовательно, НЕТ! Вы не можете получить доступ к приватным методам, но это обход.

Ответ 12

В теории Unit Testing должен быть проверен только контракт. т.е. только публичные члены класса. Но на практике разработчик обычно хочет протестировать внутренних членов. - и это неплохо. Да, это противоречит теории, но на практике это может быть полезно иногда.

Итак, если вы действительно хотите проверить внутренние члены, вы можете использовать один из этих подходов:

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

Пример кода (псевдо-код):

public class SomeClass
{
    protected int SomeMethod() {}
}
[TestFixture]
public class TestClass : SomeClass{

    protected void SomeMethod2() {}
    [Test]
    public void SomeMethodTest() { SomeMethod2(); }
}