c# юнит Разве плохо добавлять код только для модульного тестирования?




юнит тесты си (9)

Я пишу класс, чтобы помочь мне протестировать мой код. Это выглядит так:

/// <summary>
/// Wrapper for the LogManager class to allow us to stub the logger
/// </summary>
public class Logger
{
    private static ILogger _logger = null;

    /// <summary>
    /// This should be called to get a valid logger.
    /// </summary>
    /// <returns>A valid logger to log issues to file.</returns>
    public static ILogger GetLogger()
    {
        if (_logger == null)
          _logger = LogManager.GetLogger("logger");

        return _logger;
    }

    /// <summary>
    /// This is used by unit tests to allow a stub to be used as a logger.
    /// </summary>
    /// <param name="logger"></param>
    /// <returns></returns>
    public static ILogger GetLogger(ILogger logger)
    {
        _logger = logger;
        return _logger;
    }
}

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

Это плохая практика? Должен ли я найти другой способ, который не делает этого?


На мой взгляд, да, это плохая практика. Модульное тестирование предназначено для тестирования реализации вашего кода, а не для его реального влияния. В некоторых случаях я счел целесообразным организовать мой код / ​​методы таким образом, чтобы сделать его более легким / тщательным тестированием, но написание кода в классе, который тестируется для конкретного использования при тестировании, - это отдельная история.


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

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

Эти классы должны требовать ILogger в своих конструкторах, делая очевидным, что им нужно для работы, и облегчая их тестирование.


Чтобы ответить на общий вопрос «Разве это плохо, добавлять код только для модульного тестирования?», Нет, в общем, это не плохо. Тестируемость является важной особенностью системы, поскольку она помогает гарантировать правильность системы (посредством реальных испытаний) и тесно связана с другими хорошими принципами проектирования, такими как слабая связь и высокая когезия.

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

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


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


Нет, это совсем не плохо.

Хороший код должен отвечать всем требованиям и больше ничего не делать.

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

С этой точки зрения предоставление доступа к журналу тестов - отличная вещь. Намного лучше, чем оставлять непроверенными все журналы, даже если их содержание важно. Более того, ведение журнала, как правило, является наиболее важным в редких условиях, которые трудно проверить во время выполнения в «черном ящике» - модульные тесты могут быть единственным способом, которым вы можете проверить режимы отказа в нежелательном сценарии, например, скажем, на мгновение теряет связь с вашим постоянным уровнем, когда у вас есть важные данные в памяти.

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


У меня нет никаких странных конструкторов. Вместо этого у меня есть частная переменная в представлении:

private readonly ILog _log = LogManager.GetLogger(typeof(MyViewClassName));

Который затем может быть использован как таковой, в этом случае он вызывается внутри блока catch:

_log.Error("SomePage.aspx.cs Page_Load failed", ex);

Оболочек для log4net нет. ILog является частью log4net. Мне любопытно, почему вы тоже не используете Mocks. Это имело бы смысл, и тогда вам не нужно было бы делать то, что вы делаете.


Да, дизайн не идеален :( Метод GetLogger фактически устанавливает регистратор! Используйте метод setter - это выглядит более подходящим.

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

И видели библиотеку Moq? http://code.google.com/p/moq/ Мне понравилось это за насмешки.


Вы могли бы рассмотреть это:

/// <summary>
/// Wrapper for the LogManager class to allow us to stub the logger
/// </summary>
public class Logger
{
    private static ILogger _logger = null;

    /// <summary>
    /// This should be called to get a valid logger.
    /// </summary>
    /// <returns>A valid logger to log issues to file.</returns>
    public static ILogger Logger
    {
        get
        {
            if (_logger == null)
            {
                _logger = LogManager.GetLogger("logger");
            }
            return _logger
        }
        set
        {
            _logger = value;
        }
    }
}

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


Я бы не сказал, что этот конкретный кусок кода является проблемой как таковой. Пока вы заявляете в своих комментариях xmldoc, что метод (ы) предназначен только для модульного тестирования, никто (здравомыслящий!) Не будет использовать его в производственном коде.

Хотя, когда это возможно, старайтесь хранить такие вещи в своей библиотеке модульного тестирования. Как утверждает Джоэл Этертон, это кодовое раздувание, в любом случае ...





unit-testing