четверг, 27 декабря 2012 г.

Пирамида автоматизации тестирования в agile

В книге "Гибкое тестирование. Практическое руководство для тестировщиков ПО и гибких команд" Лайзы Криспин и Джанет Грегори рассказывается о пирамиде автоматизации тестирования в agile. Эта пирамида показывает идеальный вариант распределения количества автотестов по категориям. Вот что пишется об этой пирамиде в книге:

Каждый уровень характеризует количество тестов на уровне.
Unit tests – пишутся разработчиками, таких тестов самое большое количество.
API Layer  - на нем происходит тестирование бизнес-логики. Тестируется функциональность не через пользовательский интерфейс, а через API. Эти тесты пишутся на языке предметной области (domain-specific), чтобы они были понятны и разработчикам, и заказчикам. Можно использовать различные таблицы для формирования входных данных. Функциональные тесты работают напрямую с продакшн кодом, без GUI или какого-то другого слоя между ними.
GUI тесты – пишутся, когда код уже написан. В общей массе тестов их должно быть меньше остальных ( тестов нижних двух уровней пирамиды). Тестируется тонкий GUI с малым количеством или отсутствием бизнес-логики. На этом уровне не надо тестировать бизнес-функциональность. Например, можно проверить, что кнопки работают, выполняют ожидаемые действия, появляются корректные сообщения об ошибках. Какие-либо тесты, где необходимо тестировать именно пользовательский интерфейс, или когда без пользовательского интерфейса не обойтись.
Ручное тестирование (manual testing) – usability и exploratory testing. Проводится при наличии пакета регрессионных автоматизированных тестов.

На мой взгляд, это очень правильная и наглядная пирамида, но удается ли этого добиться на практике? Если мы только начинаем разрабатывать новый продукт, то такую пирамиду несомненно можно получить, если мы внедряем автоматизацию на уже существующем продукте, у которого выпущен релиз (а возможно и не один) и происходит доработка нового функционала, то скорее всего второй уровень пирамиды перейдет в третий, и станет тестами через UI.
Очень интересен второй уровень пирамиды, а точнее, кем эти тесты должны писаться. Тесты второго уровня (тесты через API) на мой взгляд должны осуществляться программистами. Потому что тестировщик-автоматизатор все таки работает на уровне пользовательского интерфейса. Несомненно он должен уметь читать код, но чтобы написать тесты на уровне API, ему придется постоянно отвлекать программистов.
В любом случае, такая пирамида отлично поможет нам в нахождении того места, где засел баг - он находится либо в каком-то модуле, либо в реализации бизнес-логики, либо в UI приложения.




четверг, 15 ноября 2012 г.

Автоматизация установки и запуска служб Windows.

При тестировании клиентской части клиент-серверных приложений мне приходилось сталкиваться с задачей автоматизации установки/удаления и запуска/остановки служб Windows, которые реализовывали серверную часть. Обычно при разворачивании и настройке приложения перед запуском всех тестов происходит установка служб, после запуска всех тестов - удаление служб (потому что мы должны привести систему в такое состояние, в котором она была до всех действий по запуску автоматизированных тестов). Также иногда в процессе выполнения тестов мне требовалось останавливать и запускать службы.

Для установки/удаления Windows-служб можно использовать следующий хелпер:


private static void ManageInstallationService(string arguments, int timeout)
        {
            const string installUtilPath = @"C:\Windows\Microsoft.NET\Framework\v4.0.30319\InstallUtil.exe";
            Process p = null;
            try
            {
                p = Process.Start(installUtilPath, arguments);
                p.WaitForExit(timeout);
                if (!p.HasExited)
                {
                    throw new TestExecutionException(String.Format(CultureInfo.InvariantCulture,
                        "Процесс установки/удаления сервиса {0} не завершился за установленный таймаут {1} мс.", arguments, timeout));
                }
            }
            finally
            {
                if (p != null && !p.HasExited)
                {
                    p.Kill();
                }
            }
        }

где arguments - абсолютный путь к устанавливаемой службе.


Для запуска/остановки Windows-служб можно использовать следующие методы:


 public static void StartService(string serviceName, int timeout)
        {
            var service = new ServiceController(serviceName);
            if (service.Status != ServiceControllerStatus.Running)
            {
                try
                {
                    var timeSpan = TimeSpan.FromMilliseconds(timeout);
                    service.Refresh();
                    service.Start();
                    service.WaitForStatus(ServiceControllerStatus.Running, timeSpan);
                }
                catch (System.ComponentModel.Win32Exception ex)
                {
                    throw new TestExecutionException(String.Format(CultureInfo.InvariantCulture,
                        "Не получилось запустить сервис «{0}».\n{1}\nService status {2}", service.DisplayName, ex.Message, service.Status));
                }
                catch (System.ServiceProcess.TimeoutException ex)
                {
                    throw new TestExecutionException(String.Format(CultureInfo.InvariantCulture,
                        "Не получилось запустить сервис «{0}».\n{1}\nService status {2}", service.DisplayName, ex.Message, service.Status));
                }
            }
        }
 
        
        public static void StopService(string serviceName, int timeout)
        {
            var service = new ServiceController(serviceName);
            if (service.Status != ServiceControllerStatus.Stopped)
            {
                try
                {
                    var timeSpan = TimeSpan.FromMilliseconds(timeout);
                    service.Refresh();
                    service.Stop();
                    service.WaitForStatus(ServiceControllerStatus.Stopped, timeSpan);
                }
                catch (System.ComponentModel.Win32Exception ex)
                {
                    throw new TestExecutionException(String.Format(CultureInfo.InvariantCulture,
                        "Не получилось остановить сервис «{0}».\n{1}\nService status {2}", service.DisplayName, ex.Message, service.Status));
                }
                catch (System.ServiceProcess.TimeoutException ex)
                {
                    throw new TestExecutionException(String.Format(CultureInfo.InvariantCulture,
                        "Не получилось остановить сервис «{0}В».\n{1}\nService status {2}", service.DisplayName, ex.Message, service.Status));
                }
            }
        }

пятница, 2 ноября 2012 г.

Проверки. Часть 3. Типы проверок.

Хочу рассказать свою точку зрения на то, какие бывают проверки и как можно выводить результаты наших проверок при выполнении автотеста. Этот вопрос уже обсуждался не один раз, и не в одном месте (например, очень хорошее обсуждение тут). Расскажу решение, которое выбрала я и применяю его в практике.

Я делю все проверки на 2 части:
1. Проверки, проверяющие промежуточные состояния.
2. Проверки, проверяющие тестируемую функциональность.

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

Проверки, проверяющие тестируемую функциональность.

 Для проверок второго типа все просто, мы используем стандартный класс любого Unit-testing фреймворка Assert или класс Verify. В таком случае результат будет выглядеть примерно так:

Assert.AreEqual failed. Expected:<6>. Actual:<-10>. После ввода некорректных данных «-10» в поле количества товара и перемещения фокуса валидация прошла некорректно.

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

Проверки, проверяющие промежуточные состояния.

Если в тесте что-то пойдет не так, например, в приложении не будет найден нужный UI-контрол, то без каких-либо проверок промежуточных состояний тест в любом случае упадет с каким-то исключением, которое будет кидать либо UI-testing фреймворк, либо код нашего теста.  Например, тест может упасть с такой ошибкой:


Test method Testing.UITests.Tests.SomeTestMethod() threw exception: 
System.ArgumentOutOfRangeException: Индекс за пределами диапазона. Индекс должен быть положительным числом, а его размер не должен превышать размер коллекции.
Имя параметра: index

или с такой


Метод проверки Testing.UITests.OrganizationList.ViewOrganizationsPropertiesTestFixture.ПросмотрИнформацииОПредприятии выдал исключение: Microsoft.VisualStudio.TestTools.UITest.Extension.UITestControlNotFoundException: Дополнительные сведения При воспроизведении не удалось найти элемент управления с указанными свойствами поиска.: 
TechnologyName:  "UIA"
ControlType:  "Edit"
AutomationId:  "24763E55-DB6E-4A65-9545-5301D251FC72"

Но эти сообщения об ошибках не очень человеко-читаемы. Для вывода более понятных сообщений можно использовать проверки промежуточного состояния. Но для этого лучше использовать не Asssert, а бросать какое-либо исключение с понятным сообщением. Что нам это даст - при просмотре результатов запуска тестов в случае падения нам сразу будет понятно, что ошибка произошла не при проверке тестируемой функциональности. Понятное сообщение об ошибке может сразу подсказать нам, в чем именно заключается ошибка в тесте (например, поменялся AutomationId у контрола, либо немного поменялся сам ход сценария, например, раньше мы принимали за данное, что фокус уже стоит на каком-то элементе, а новое поведение - фокус на элемент надо ставить вручную). Можно использовать либо стандартные исключения, либо создать какое-то свое пользовательское и кидать его. Например,  я могу создать исключение TestExecutionException и кидать его в случае fail'а теста из-за невыполнения промежуточной проверки.


Пример (C#). Нам на определенном шаге теста нужно проверить, что таблица наименований товара не пустая.

if (Goods.Count == 0)
{
  throw new TestExecutionException("В таблице товаров нет ни одного товара.");
}

Тогда текст сообщения об ошибке будет следующим:

Test method Testing.UITests.Tests.SomeTestMethod() threw exception:
Testing.Common.TestExecutionException: В таблице товаров нет ни одного товара

Если лень читать Msdn, вот как создать пользовательское исключение:

    [Serializable]
    public class TestExecutionException : Exception
    {
        public TestExecutionException()
        {
        }
 
        public TestExecutionException(string message)
            : base(message)
        {
        }
 
        public TestExecutionException(string message, Exception innerException)
            : base(message, innerException)
        {
        }
 
        protected TestExecutionException(SerializationInfo info, StreamingContext context) : base(info, context)
        {
        }
    }




четверг, 25 октября 2012 г.

Проверки. Часть 2. Сравнение сущностей


Представим, что у сравниваемых сущностей очень много свойств, хотя бы 5-10. В таком случае, если пользоваться вариантом, рассказанным в предыдущем сообщении, и если тест не пройдет, придется потратить какое-то время, чтобы из текста сообщения об ошибке определить, какие же свойства не совпали.  А нам так бы хотелось сравнивать объекты,  но чтобы в результате были показаны неожидаемые свойства сущности.

Проверки. Часть 1. Сравнение сущностей.

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

четверг, 18 октября 2012 г.

Как сделать TextBlock внутри DataTemplate видимым для UIAutomation

Это вольный пересказ заметки http://www.wpftutorial.net/UIAutomation.html.

При UI автоматизации desktop приложений, мы можем столкнуться с ситуацией, когда TextBlock не является автоматизируемым контролом. Мы видим его в приложении, но при просмотре структуры автоматизируемых контролов через UISpy, он в ней не отображается. Это происходит в тех случаях, когда в xaml-коде окна TextBlock находится внутри DataTemplate. WPF специально скрывает такие TextBlock для увеличения производительности. Чтобы сделать их видимыми, нужно либо заменить их на контрол Label, что может ухудшить производительность, либо сделать специальный автоматизируемый UIAutomationTextBlock   на основе существующего TextBlock, и использовать в xaml-коде окна его.

вторник, 16 октября 2012 г.

Выступление на автоконфетке

Вот и состоялось мое первое в жизни выступление на конференции. Ею была онлайн-конференция по автоматизированному тестированию АвтоКонфетка. Всегда хотела на ней выступить, и оказалось, это реально, главное захотеть. Я выступала с докладом "Использование SpecFlow для написания тестов на русском языке". Мне очень хотелось рассказать за эти 20 минут как можно больше, потому что со всем этим я сталкивалась, и все это пригодится при автоматизации реального проекта.  Сделала для себя определенные выводы. Смущает, что вроде бы конференция, но на форуме особо обсуждений и нет. Надеюсь, хотя бы для кого-то эта информация будет полезной.  До встречи!

пятница, 21 сентября 2012 г.

Team Foundation Server 2010. Настройка workflow Build-Deploy-Test для физического окружения.

Хочу поделиться со всеми моим опытом использования Team Foundation Server 2010 и Lab Management для настройки workflow Build-Deploy-Test. В итоге все получается очень легко и красиво, пару кликов мышкой, и вы забываете про процесс сборки проектов, деплоймента и запуска тестов. Все это делается за вас, а на выходе получается наглядный результат по запуску тестов. Разве не круто!

TFS 2010. Настройка workflow Build-Deploy-Test

четверг, 16 августа 2012 г.

Verify вместо Assert



В функциональных тестах правило юнит-тестов «один тест – одна проверка» делеко не всегда применимо. Бывают случаи, когда хочется проверить сразу несколько незавимых друг от друга вещей в тесте и получить результат, даже в случае непрохождения одной из проверок.

Автоматизировать - это классно!

Всем привет! Вот и дошли наконец-то мои ручки до первой записи в моем блоге :) Юхууу!!!

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

В своем блоге я буду рассказывать, как писать автоматизированные тесты и вообще применять автоматизацию в своей повседневной работе. Welcome and enjoy :)