Модульное тестирование - не тестируемый код, преобразованный в тестируемый код - PullRequest
4 голосов
/ 23 декабря 2010

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

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

ТИА

Ответы [ 4 ]

7 голосов
/ 23 декабря 2010

Вот две замечательные книги, которые помогут вам начать:

  1. Искусство модульного тестирования
  2. Эффективная работа с устаревшим кодом

Удачи.

1 голос
/ 23 декабря 2010

Важнейшим ключевым фактором, делающим код более тестируемым (единичным), является внедрение зависимостей.

Первая глава книги Марка Симанна

Внедрение зависимостей в .NET

находится в свободном доступе здесь http://www.manning.com/seemann/ в виде файла PDF. Он содержит очень полный пример того, как сделать часть тесно связанного кода более тестируемой за счет уменьшения зависимостей от нижних уровней.

1 голос
/ 23 декабря 2010

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

protected void buttonClick(object sender, EventArgs e)
{
    string currUser =
        User.Identity.Name.ToString().Trim()
            .Substring(User.Identity.Name.ToString().Trim()
            .IndexOf("\\") + 1);

    Inventory.Employee.DB objEmpDB = new Inventory.Employee.DB();
    Inventory.Employee.Details objEmpDetails = 
        new Inventory.Employee.Details();

    objEmpDetails = objEmpDB.Get(currUser);

    Welcome.Text = 
        "Current User: " + objEmpDetails.Employee_Full_Name;

    var objUserDetails = new Inventory.User.Details();
    Inventory.User.DB objUserDB = new Inventory.User.DB();

    if (objUserDB.UserAuthenticates(currUser))
    {
        objUserDetails = objUserDB.Get(currUser);
        currUserToken = objUserDetails.User_Token.Value;

        userID.Text = currUser;

        if (objUserDetails.Active_User_Name != objUserDetails.User_Name)
        {
            lShadow.Text = "Showin: " + objUserDetails.Active_User_Name;
            lServer.Text = "(" +
            objUserDB.UserPermissionName(objUserDetails.Active_Logon_Name)
                + ") - " + System.Environment.MachineName;
            lShadow.ToolTip = Inventory.Properties.Settings.Default
                .connectionString.Substring(0, Inventory.Properties
                .Settings.Default.connectionString.IndexOf(';'));
            divShadow.Visible = true;
        }
        else
            divShadow.Visible = false;

        lWelcome.Text = "Current User: " + objUserDetails.User_Name;
    }
}

Мало того, что это сложно из-за сложности эмуляции щелчка пользовательской кнопки, но и посмотрите, сколько всего происходит при нажатии этой кнопки. Если ваш модульный тест не пройден, есть около 100 странных вещей, которые могли пойти не так. СУХОЙ, одиночный подход и другие принципы дизайна приводят к коду, который легко тестировать и легко исправить. В конце концов, что хорошего в юнит-тесте, если вы тестируете бригады, а не юниты:)

ОБНОВЛЕНИЕ: (Как исправить вышеуказанный код)
Я не собираюсь делать вид, что приведенный выше код легко исправить. Это «маленький» пример из базы кода, над которой я работал в прошлом. Я хотел показать, как плохие вещи могут быть в реальной жизни.

Есть две основные проблемы с кодом.

  1. Его сложно проверить нажатием кнопки События.
  2. Слишком много всего происходит в одном методе.

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

protected void buttonClick(object sender, EventArgs e)
{
   EasyToCallMethod();
}

public void EasyToCallMethod()
{
    string currUser =
        User.Identity.Name.ToString().Trim()
        .Substring(User.Identity.Name.ToString().Trim().IndexOf("\\") + 1);
    //...rest of code
}

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

Easy Fix
Итак, есть хорошие 15-20 тестов, которые мы можем сделать из этого одного вызова метода. Просто сделайте тест для каждой строки, которая имеет определенное назначение (например, где выполняются вызовы методов), и у вас должны быть хорошие модульные тесты, достаточно маленькие, чтобы определить, где что-то сломалось, и хорошее покрытие кода. Продвинутые вещи
Гораздо больше работы можно сделать. Мы можем реализовать n-уровневый MVC или MVVM. В какой-то момент вы должны спросить себя, если вы чрезмерно инженерии. Модульные тесты должны сделать ваш код более понятным, но не переусердствовать в ничто. Это где ваш собственный стиль и опыт вступают в игру. Когда вы чувствуете, что у вас есть основы, вы должны вернуться к SO с новыми вопросами или взять хорошую книгу.

1 голос
/ 23 декабря 2010

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

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