Основы тестирования программного обеспечения

Ручное тестирование

Разбить на страницы
Показывать лекцию целиком

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

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

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

Кроме того, необходимо сохранять получаемую от сервера информацию о функционировании системы ( выходные данные ) в журнале (рис. 5.1).

(рис 5.1) Система и ее окружение (ручное тестирование)

При разработке тестов был использован следующий подход:

  • состояние окружения задается в тесте (входные данные);
  • в тесте создается сервер:
  • сервер по запросу от dll передает информацию о заданном состоянии окружения;
  • сервер получает от dll информацию о функционировании системы;
  • получаемая информация сохраняется в журнале теста.
  • Подробное описание тестового случая №1

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

    class Test1:Test {
    override public void start()
    {//Задаем состояние окружения (входные данные)
    StoreStat="32"; //Поступил подшипник
    RollerPar="0 NewUser Depot1 123456 1 12 1 1";
    //статус обмена с терминалом подшипника (0 - есть подшипник)
    //и его параметры
    AxlePar="1 NewUser Depot1 123456 1 0 12 12";
    //статус обмена с терминалом оси (1 - нет оси) и ее параметры
    CommandStatus="0"; //команда успешно принята
    StoreMessage="1"; //успешно выполнена
    //Получаем информацию о функционировании системы
    wait("GetStoreStat"); //опрос статуса склада
    wait("GetRollerPar");
    //Получение информации о подшипнике с терминала подшипника
    wait("GetAxlePar");
    //Получение информации об оси с терминала оси
    wait("SendStoreCom");
    //добавление в очередь команд склада на первое место
    //команды GetR (получить из приемника в ячейку)
    wait("GetStoreMessage");
    //Получение сообщения от склада о результатах выполнения команды
    //В результате первый подшипник должен быть принят
    wait("GetStoreStat"); //опрос статуса склада
    wait("GetRollerPar");
    //Получение информации о подшипнике с терминала подшипника
    wait("GetAxlePar");
    //Получение информации об оси с терминала оси
    wait("SendStoreCom");
    //добавление в очередь команд склада на первое место
    //команды GetR (получить из приемника в ячейку)
    wait("GetStoreMessage");
    //Получение сообщения от склада о результатах выполнения
    //команды. В результате второй подшипник должен быть принят
    //Задаем новое состояние окружения (входные данные)
    RollerPar="1 NewUser Depot1 123456 1 12 1 1";
    //статус обмена с терминалом подшипника (1 - нет подшипника)
    //и его параметры
    AxlePar="0 NewUser Depot1 123456 1 0 12 12";
    //статус обмена с терминалом оси (0 - есть ось) и ее параметры
    //Получаем информацию о функционировании системы
    wait("GetStoreStat"); //опрос статуса склада
    wait("GetRollerPar");
    //Получение информации о подшипнике с терминала подшипника
    wait("GetAxlePar");
    //Получение информации об оси с терминала оси
    wait("SendStoreCom");//Добавление в очередь команд склада на
    последнее место //команды SendR (ячейку на выход)
    wait("GetStoreMessage");
    //Получение сообщения от склада о результатах выполнения
    //команды
    //В результате первый подшипник для оси должен быть выдан
    wait("SendStoreCom");
    //Добавление в очередь команд склада на последнее место
    //команды SendR (ячейку на выход)
    wait("GetStoreMessage");
    //Получение сообщения от склада о результатах выполнения
    //команды.
    //В результате второй подшипник для оси должен быть выдан
    wait("SendStoreCom");
    //Добавление в очередь команд склада на последнее место
    //команды Term (завершение команд выдачи)
    wait("GetStoreMessage");
    //Получение сообщения от склада о результатах выполнения
    //команды
    finish();
    }
    }

    При разработке тестов не обязательно дожидаться каждого события, которое должно происходить в соответствии со случаем использования. Достаточно вызвать wait для событий, после наступления которых надо менять состояние окружения. В период ожидания наступления события, заданного в wait, может происходить любое количество других событий. Все эти события будут занесены в журнал. При необходимости ждать не первого, а n-го вызова можно вызывать wait с одним и тем же параметром n раз (например, в цикле). При таком подходе тест будет гораздо короче, например приведенный выше тест будет выглядеть следующим образом:

    class Test1:Test
    {
    override public void start()
    {
    StoreStat="32";//Пришел подшипник
    RollerPar="0 NewUser Depot1 123456 1 12 1 1";//его параметры
    AxlePar="1 NewUser Depot1 123456 1 0 12 12";//нет оси
    CommandStatus="0";//команда успешно принята
    StoreMessage="1";//команда успешно выполнена
    wait("SendStoreCom");//первый подшипник принят
    wait("SendStoreCom");//второй подшипник принят
    RollerPar="1 NewUser Depot1 123456 1 12 1 1";
    //больше нет подшипников
    AxlePar="0 NewUser Depot1 123456 1 0 12 12";//есть ось
    wait("SendStoreCom");//выдача подшипника
    wait("SendStoreCom");//выдача подшипника
    wait("SendStoreCom");//завершение выдачи
    finish();}
    }

    Описание тестовых процедур

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

    Как запустить тест

    Для того чтобы запустить тест, нужно:

    В методе Class1.Main создать экземпляр нового класса и вызвать его метод start. Так как метод start является виртуальным, то можно обращаться к тестам, используя ссылку на тип Test. Это делается так:

    Test t = new <ваш класс-потомок Test>;
    t.start();

    Собрать и запустить приложение.

    Проверка результатов выполнения тестов (сравнение с ожидаемым результатом)

    После завершения теста следует просмотреть текстовый журнал теста ( ..\SystemTesting\ManualTests\Tests\bin\Debug\log.log ), чтобы выяснить, какая последовательность событий в системе была реально зафиксирована (выходные данные) и сравнить их с ожидаемыми результатами, заданными в спецификации тестового случая №1.

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

    Пример неправильного теста

    Предположим, что вы не поняли Приложение 2 (FS) во всех деталях и неправильно составили спецификацию тестового случая №1 и тест. Например:

    //Задаем состояние окружения (входные данные)
    StoreStat="32"; //Поступил подшипник
    ...
    //Получаем информацию о функционировании системы
    wait("GetStoreStat"); //опрос статуса склада
    //Вместо того, чтобы получить информацию о подшипнике с
    //терминала подшипника, мы хотим получить информацию
    //об оси с терминала оси
    wait("GetAxlePar");
    ...

    В журнале теста мы увидим следующую информацию:

    CALL: GetStoreStat 0
    RETURN: 32
    CALL: GetRollerPar
    RETURN: 0 NewUser Depot1 123456 1 12 1 1
    CALL: GetAxlePar
    RETURN: 1 NewUser Depot1 123456 1 0 12 12
    ...

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

    Задание 3

    Для тестового случая №1 необходимо составить полный список всех возможных альтернативных путей (см. подраздел "Список альтернативных путей") и разработать соответствующие тесты.

    Кроме того, необходимо:

  • выбрать случай использования на основании дерева решений ( ..\SystemTesting\Decision Tree.vsd );
  • составить пошаговое описание выбранного случая использования;
  • учесть все альтернативные пути;
  • составить спецификации тестовых случаев;
  • разработать соответствующие тестовые случаи (тесты) ;
  • составить тестовые отчеты (табл. 2.2).
  • Страницы:

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

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

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

    Кроме того, необходимо сохранять получаемую от сервера информацию о функционировании системы ( выходные данные ) в журнале (рис. 5.1).

    (рис 5.1) Система и ее окружение (ручное тестирование)

    При разработке тестов был использован следующий подход:

  • состояние окружения задается в тесте (входные данные);
  • в тесте создается сервер:
  • сервер по запросу от dll передает информацию о заданном состоянии окружения;
  • сервер получает от dll информацию о функционировании системы;
  • получаемая информация сохраняется в журнале теста.
  • Подробное описание тестового случая №1

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

    class Test1:Test {
    override public void start()
    {//Задаем состояние окружения (входные данные)
    StoreStat="32"; //Поступил подшипник
    RollerPar="0 NewUser Depot1 123456 1 12 1 1";
    //статус обмена с терминалом подшипника (0 - есть подшипник)
    //и его параметры
    AxlePar="1 NewUser Depot1 123456 1 0 12 12";
    //статус обмена с терминалом оси (1 - нет оси) и ее параметры
    CommandStatus="0"; //команда успешно принята
    StoreMessage="1"; //успешно выполнена
    //Получаем информацию о функционировании системы
    wait("GetStoreStat"); //опрос статуса склада
    wait("GetRollerPar");
    //Получение информации о подшипнике с терминала подшипника
    wait("GetAxlePar");
    //Получение информации об оси с терминала оси
    wait("SendStoreCom");
    //добавление в очередь команд склада на первое место
    //команды GetR (получить из приемника в ячейку)
    wait("GetStoreMessage");
    //Получение сообщения от склада о результатах выполнения команды
    //В результате первый подшипник должен быть принят
    wait("GetStoreStat"); //опрос статуса склада
    wait("GetRollerPar");
    //Получение информации о подшипнике с терминала подшипника
    wait("GetAxlePar");
    //Получение информации об оси с терминала оси
    wait("SendStoreCom");
    //добавление в очередь команд склада на первое место
    //команды GetR (получить из приемника в ячейку)
    wait("GetStoreMessage");
    //Получение сообщения от склада о результатах выполнения
    //команды. В результате второй подшипник должен быть принят
    //Задаем новое состояние окружения (входные данные)
    RollerPar="1 NewUser Depot1 123456 1 12 1 1";
    //статус обмена с терминалом подшипника (1 - нет подшипника)
    //и его параметры
    AxlePar="0 NewUser Depot1 123456 1 0 12 12";
    //статус обмена с терминалом оси (0 - есть ось) и ее параметры
    //Получаем информацию о функционировании системы
    wait("GetStoreStat"); //опрос статуса склада
    wait("GetRollerPar");
    //Получение информации о подшипнике с терминала подшипника
    wait("GetAxlePar");
    //Получение информации об оси с терминала оси
    wait("SendStoreCom");//Добавление в очередь команд склада на
    последнее место //команды SendR (ячейку на выход)
    wait("GetStoreMessage");
    //Получение сообщения от склада о результатах выполнения
    //команды
    //В результате первый подшипник для оси должен быть выдан
    wait("SendStoreCom");
    //Добавление в очередь команд склада на последнее место
    //команды SendR (ячейку на выход)
    wait("GetStoreMessage");
    //Получение сообщения от склада о результатах выполнения
    //команды.
    //В результате второй подшипник для оси должен быть выдан
    wait("SendStoreCom");
    //Добавление в очередь команд склада на последнее место
    //команды Term (завершение команд выдачи)
    wait("GetStoreMessage");
    //Получение сообщения от склада о результатах выполнения
    //команды
    finish();
    }
    }

    При разработке тестов не обязательно дожидаться каждого события, которое должно происходить в соответствии со случаем использования. Достаточно вызвать wait для событий, после наступления которых надо менять состояние окружения. В период ожидания наступления события, заданного в wait, может происходить любое количество других событий. Все эти события будут занесены в журнал. При необходимости ждать не первого, а n-го вызова можно вызывать wait с одним и тем же параметром n раз (например, в цикле). При таком подходе тест будет гораздо короче, например приведенный выше тест будет выглядеть следующим образом:

    class Test1:Test
    {
    override public void start()
    {
    StoreStat="32";//Пришел подшипник
    RollerPar="0 NewUser Depot1 123456 1 12 1 1";//его параметры
    AxlePar="1 NewUser Depot1 123456 1 0 12 12";//нет оси
    CommandStatus="0";//команда успешно принята
    StoreMessage="1";//команда успешно выполнена
    wait("SendStoreCom");//первый подшипник принят
    wait("SendStoreCom");//второй подшипник принят
    RollerPar="1 NewUser Depot1 123456 1 12 1 1";
    //больше нет подшипников
    AxlePar="0 NewUser Depot1 123456 1 0 12 12";//есть ось
    wait("SendStoreCom");//выдача подшипника
    wait("SendStoreCom");//выдача подшипника
    wait("SendStoreCom");//завершение выдачи
    finish();}
    }

    Описание тестовых процедур

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

    Как запустить тест

    Для того чтобы запустить тест, нужно:

    В методе Class1.Main создать экземпляр нового класса и вызвать его метод start. Так как метод start является виртуальным, то можно обращаться к тестам, используя ссылку на тип Test. Это делается так:

    Test t = new <ваш класс-потомок Test>;
    t.start();

    Собрать и запустить приложение.

    Проверка результатов выполнения тестов (сравнение с ожидаемым результатом)

    После завершения теста следует просмотреть текстовый журнал теста ( ..\SystemTesting\ManualTests\Tests\bin\Debug\log.log ), чтобы выяснить, какая последовательность событий в системе была реально зафиксирована (выходные данные) и сравнить их с ожидаемыми результатами, заданными в спецификации тестового случая №1.

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

    Пример неправильного теста

    Предположим, что вы не поняли Приложение 2 (FS) во всех деталях и неправильно составили спецификацию тестового случая №1 и тест. Например:

    //Задаем состояние окружения (входные данные)
    StoreStat="32"; //Поступил подшипник
    ...
    //Получаем информацию о функционировании системы
    wait("GetStoreStat"); //опрос статуса склада
    //Вместо того, чтобы получить информацию о подшипнике с
    //терминала подшипника, мы хотим получить информацию
    //об оси с терминала оси
    wait("GetAxlePar");
    ...

    В журнале теста мы увидим следующую информацию:

    CALL: GetStoreStat 0
    RETURN: 32
    CALL: GetRollerPar
    RETURN: 0 NewUser Depot1 123456 1 12 1 1
    CALL: GetAxlePar
    RETURN: 1 NewUser Depot1 123456 1 0 12 12
    ...

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

    Задание 3

    Для тестового случая №1 необходимо составить полный список всех возможных альтернативных путей (см. подраздел "Список альтернативных путей") и разработать соответствующие тесты.

    Кроме того, необходимо:

  • выбрать случай использования на основании дерева решений ( ..\SystemTesting\Decision Tree.vsd );
  • составить пошаговое описание выбранного случая использования;
  • учесть все альтернативные пути;
  • составить спецификации тестовых случаев;
  • разработать соответствующие тестовые случаи (тесты) ;
  • составить тестовые отчеты (табл. 2.2).
  • Вернуться к учебному плану