Показаны сообщения с ярлыком Тесты. Показать все сообщения
Показаны сообщения с ярлыком Тесты. Показать все сообщения

среда, 25 июня 2014 г.

Тестируем калькулятор №7. Сравнение чисел с плавающей запятой. Детальнее об архитектуре тестов

Оглавление всей серии постов о тестировании калькулятора.
Нарисовав диаграмму классов к прошлой главе, я заметил что у меня есть класс TRandomPlusTest который я не видел в GUI DUnit'a.

четверг, 19 июня 2014 г.

Тестируем калькулятор № 6.2.2. Обработка неверных данных и граничных условий

Стоило мне написать про ночи без звонков. Как это сразу и случилось. Заказчик прислал такую вот картинку:


В общем, я конечно же не учел деления на ноль.
На мой удивленный вопрос - "А Вы знаете что на 0 делить нельзя ?"
Клиент заявил что - "Необходимо что бы калькулятор писал "Деление на 0" в ответе".


четверг, 12 июня 2014 г.

Тезисы. Тесты. Как я их вижу

Список будет пополняться.

Что такое тесты?

1. Тесты это средство проверки архитектуры. (Если архитектура "тестируема", то она - "хороша").
2. Тесты это средство документации кода. (Если к куску кода написан тест, то это значит, что дан пример использования).
3. Как следствие из предыдущего пункта - тесты это средство КОММУНИКАЦИИ между разработчиками.
4. Тесты это инфраструктура разработки. (Обеспечивается возможность "рафинированных" вызовов и "специальной подготовки данных").
5. Тесты это средство выявления противоречивостей в ТЗ. (Если два или более тестов "не сходятся" одновременно, то это скорее всего проблема в ТЗ).
6. Тесты это средство проверки детерминированности кода. (Повторные запуски одинаковых тестов для детерминированного кода должны "сходиться").
7. Тесты это средство выявления регресса.
8. Тесты это средство проверки корректности логики работы.

Что такое атомарные тесты?
1. Это средство проверки корректности логики работы. (Согласно ТЗ).

Что такое тесты с эталонами?
1. Это средство проверки детерминимрованности кода.
2. Это средство выявления регресса.

Что такое с тесты псевдослучайными данными?
1. Это средство расширения тестового покрытия.

среда, 11 июня 2014 г.

Тестируем калькулятор №6.2.1. Применяем "классическое TDD"


В нашей новой главе мы попробуем применить "классическое TDD" при разработке новой операции нашего калькулятора.
Наш заказчик сообщил нам что хочет что-бы калькулятор “умел” делать целочисленное деление. При этом заплатил нам аванс, уточнил что готово это должно быть на завтра, и исчез не дав нам задать не одного вопроса.


вторник, 10 июня 2014 г.

Тестирование калькулятора. Оглавление

Глава 6.1. Тестирование с использованием эталонов.
Глава 6.2.Тестирование с использованием псевдослучайных данных.
Глава 6.2.1. Применяем "классическое TDD".
Глава 6.2.2. Обработка неверных данных и граничных условий.
Глава 6.2.2. Приложение. UML диаграмма классов тестирования.
Глава 7. Сравнение чисел с плавающей запятой, детальнее об архитектуре тестов


План будущих статей.


Тезисы о тестах.

Пояснения о "непоказательности примера".
Тесты как "пример использования кода".

Ну и закольцуем - Как тестировать "нетестируемые" приложения.


Диаграмма класов UML.



Ну и Offtopic - Коротко. Нельзя никого насильно сделать счастливым.

Ну и НЕ Offtopic - Коротко. Про тесты.

Ну и ещё - Про пред- и пост-условия. "Другое" мнение.

вторник, 3 июня 2014 г.

Тестируем калькулятор №6.2.1. Применяем "классическое TDD" (анонс)

Предыдущая серия была тут - http://programmingmindstream.blogspot.ru/2014/06/62.html

Промежуточные итоги я постарался сформулировать вот тут - http://programmingmindstream.blogspot.ru/2014/06/62.html?showComment=1401745726422#c664156170645232329

Теперь мы поговорим о "классическом TDD", а именно - о его части - Test First.

В предыдущих главах мы сделали вот что:

1. Настроили ИНФРАСТРУКТУРУ тестирования.
2. Сделали GUI-тесты.
3. Сделали тесты бизнес-логики.
4. Сделали тесты бизнес-логики с использованием эталонов и ПРОТЕСТИРОВАЛИ (худо-бедно) работу эталонов.
5. Из тестов бизнес-логики и работы с эталонами при помощи псевдослучайных данных мы сделали РЕГРЕССИОННЫЕ тесты.

Теперь давайте попробуем применить "классическое TDD".

понедельник, 2 июня 2014 г.

Тестируем калькулятор № 6.2. Тестирование с использованием псевдослучайных данных

Имея в распоряжении 4 теста для 4 операций мы точно не проверяем "большой разброс" во входных данных. Для того чтобы расширить наше тестовое покрытие мы переходим к нашей новой главе - "Тестированию с использованием псевдослучайных данных". Как следует из названия, нам необходим какой-то случайный набор данных для тестирования. Результаты тестирования при этом будут записываться в выходной файл, так же как мы делали это в прошлой главе. 

Для начала проведем небольшой рефакторинг нашего класса TCalculatorOperationViaEtalonTest

четверг, 29 мая 2014 г.

Тестируем калькулятор № 6.1. Тестирование с использованием эталонов

Глава 0.
Глава 1.
Глава 2.
Глава 3.
Глава 4.
Глава 5.

В прошлой главе мы перешли от тестирования GUI формы, к тестированию бизнес-логики, выделив класс бизнес логики TCalculator.

В этой главе мы обсудим и внедрим “Тестирование с использованием эталонов”. Тестирование с использованием эталонов строится на базе тестов из прошлой главы. Однако выполняет более важную функцию, о чем будет рассказано далее. В двух словах -использование эталонов предполагает сохранение значений и результата теста в файл, который мы затем сравниваем с эталонным. Если файлы не совпадают то тест “провалился”. Тут возникает вопрос откуда мы возьмем эталонный файл? И здесь у нас 2 варианта: Либо мы его создадим руками, либо как поступил я - если эталона не существует, то мы создаем его автоматически на основе файла результата тестирования, так как допускаем что тесты у нас заведомо правильные.

Постараюсь более детально объяснить на примере написания эталонного теста к операции + или как она записана у нас в калькуляторе функция ADD.

среда, 21 мая 2014 г.

Тестируем калькулятор №5. Тесты через "новую архитектуру"

Здравствуйте читатели.
Александр любезно предоставил мне  "площадку" для понимания TDD.
Постижение написания кода через тестирование под руководством опытного наставника... Что может быть приятней :).

Небольшая преамбула:
18 февраля Всеволод Леонов предложил Александру показать каким образом приложение которое не задумывалось для тестирования превратить в full-testable system.
Приложением для примера был выбран обычный калькулятор:
Проект был банален до невозможности, 4 кнопки 3 Edit.

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

Первым делом добавляем к приложению тесты(DUnit), детали описаны здесь.
Следующим шагом Александр "посещает форму, присваивает 2 значения Едитам и проверяет кнопку Плюс".
Третий этап означал расширение тестов от одной операции(Плюс) до покрытия всех кнопок. Александр при этом выделил абстрактный класс - TOperationTest. В комментариях к этому посту развернулась довольно интересная дискуссия по поводу выделения абстрактного класса, зачем и почему читатели узнают из будущих постов :)
В четвертом посте Александр выделил класс "бизнес-логики" -  TCalculator, в котором находятся функции для расчета всех операций калькулятора.

Ну а сегодня мы обсудим тестирование логики нашего приложения.
После изменений архитектуры мы можем протестиорвать непосредственно "логику приложения". Поэтому. Самый простой способ сделать это сейчас это написать класс TCalculatorOperation отнаследованный от TTestCase и проверить все операции. Я правда ещё и проверил ошибочность выполнения в одной из операций. Но я думаю что если читателям будет интересно то эту тему мы обсудим в комментариях.
Собственно код:
unit CalculatorOperationTest;

interface

uses
  TestFrameWork,
  Calculator
  ;

 type
  TCalculatorOperationTest = class(TTestCase)
   published
    procedure LogicTestDiv;
    procedure LogicTestMul;
    procedure LogicTestAdd;
    procedure LogicTestSub;
    procedure LogicTestSubError;
  end;//TCalculatorOperationTest

implementation

  uses
   SysUtils;

const
 cA = '5';
 cB = '10';
{ TCalculatorOperationTest }

procedure TCalculatorOperationTest.LogicTestDiv;
var
  x1, x2  : string;
  result : Single;
begin
  x1:= cA;
  x2:= cB;
  result := StrToFloat(TCalculator.Divide(x2, x1));
  CheckTrue(2 = result);
end;

procedure TCalculatorOperationTest.LogicTestSub;
var
  x1, x2  : string;
  result : Single;
begin
  x1:= cA;
  x2:= cB;
  result := StrToFloat(TCalculator.Sub(x2, x1));
  CheckTrue(5 = result);

end;

procedure TCalculatorOperationTest.LogicTestSubError;
var
  x1, x2  : string;
  result : Single;
begin
  x1:= cA;
  x2:= cB;
  result := StrToFloat(TCalculator.Sub(x2, x1));
  CheckFalse(7 = result);
end;

procedure TCalculatorOperationTest.LogicTestMul;
var
  x1, x2  : string;
  result : Single;
begin
  x1:= cA;
  x2:= cB;
  result := StrToFloat(TCalculator.Mul(x2, x1));
  CheckTrue(50 = result);
end;

procedure TCalculatorOperationTest.LogicTestAdd;
var
  x1, x2  : string;
  result : Single;
begin
  x1:= cA;
  x2:= cB;
  result := StrToFloat(TCalculator.Add(x2, x1));
  CheckTrue(15 = result);
end;

initialization
 TestFramework.RegisterTest(TCalculatorOperationTest.Suite);
end.

Таким образом мы ушли от "тестирования GUI" к тестированию "бизнес логики".


upd1. Залил исходнки на BitBucket.

среда, 5 марта 2014 г.

План будущих статей

Знаете...

Ужасно удручён и расстроен я событиями и ситуацией на Украине.

Расстроен и за "наших", и за "не наших".

Посему - депрессия. Что-то плохо всё пишется.

Но планы на самом деле - большие. Если это кому-то нужно в этом УЖАСНОМ мире.

Вот что в планах:

1. Тестируем калькулятор №5. Тесты через "новую архитектуру".
2. Тестируем калькулятор №6. Использование эталонов. Рандомные тесты.
3. Тестируем калькулятор №7. Как правильно сравнивать double. Мак-Конел, и Мак-Кракен и Дорн.
4. Тестируем калькулятор №8. Параметризованные тесты.
5. Почему IStream и IDataObject это ужасно с "архитектурной точки зрения" и как с этим бороться.
6. Автоматические ночные сборки.
7. О практике использования Mutable и Immutable строк. Да и прочих объектов. И почему в Delphi не хватает модификатора const НА МЕТОДАХ объектов.
8. О разнице в интерфейсной и поведенческой эквивалентности в классах, примесях и интерфейсах. И за что я люблю ТИПИЗИРОВАННЫЙ Duck-Typing.
9. Абстрактные классы и как не давать их создавать.
10. Assert'ы и ТЗ.
11. Почему "отдельностоящие методы" ЛУЧШЕ методов КЛАССОВ/интерфейсов и как ЭТО ПОНИЖАЕТ "связность". Повод для данной статьи вот - http://programmingmindstream.blogspot.ru/2014/03/blog-post_12.html
12. Тестируем калькулятор №9. Готовимся к расширению функциональности. Через Dependency Injection. Выделяем класс "TCalculatorOperation".
13. Тестируем калькулятор №10. Расширяем функциональность. От класса TCalculator переходим ко МНОЖЕСТВУ классов TCalculatorOperation.
14. Тестируем калькулятор №11. Размножаем тесты относительно какого-то заданного "итератора". Например по операциям калькулятора.
15. Тестируем калькулятор №12. Тесты и примеси. Скажем так опять.
16. Тестируем калькулятор №13. От класса TCalculatorOperation переходим к "аксиоматике", скриптовой машине и скриптам.
17. Тестируем калькулятор №14. На основе Dependency Injection и "аксиоматике" делаем ДВЕ "морды" калькулятора - на VCL и на FM. Опционально - можно ещё и строковый интерфейс забабахать.
18. Про "инструменты" (тегов) или паттерн Strategy (или Template Method, или Observer). Как они позволяют "внедрять функциональность "сбоку"".
19. По мотивам предыдущей темы - паттерн Context и как не привязываться к  "состояниям" "инструментов". Как делать инструменты объектами БЕЗ СОСТОЯНИЙ (Immutable).
20. По мотивам предыдущей темы - как делать "инструменты" не только объектами и/или интерфейсами, но и class of или Event или даже reference to. И как это позволяет "экономить на спичках", да и не только на них.
21. Ну и ещё по мотивам предыдущих тем привести пример, про TextPara, ParaList, Owner'ов и "декорации" для "баллонов". Почему лучше подавать Owner'а "сверху" (в частности для Painter'ов и HotSpot'ов), а не пытаться получить его "изнутри".

четверг, 28 ноября 2013 г.

Об асинхронности и модальности в GUI тестах

Об асинхронности и модальности в GUI тестах

О тестах GUI я писал тут - http://18delphi.blogspot.ru/2013/11/gui.html ну и "в последующих сериях".

Сегодня я размышлял над одной хорошей заметкой моего товарища по цеху - Романа Янковского.

О Awaitable-значениях (http://roman.yankovsky.me/?p=1100).

И неожиданно понял - что ещё я хочу сказать о GUI-тестировании.

Есть два момента с которыми рано или поздно сталкиваются GUI-тестировщики. Когда система входит в такое состояние, когда она блокирует управление и крутит какой-то "внутренний цикл".

А именно:

1. Например показ контекстного меню или что-то подобное.
2. Показ модального диалога и ожидание ввода пользователя.

И это далеко не самая простая проблема.

Ведь код теста - "замораживается" и система "ждёт ввода от пользователя" и тесты на это повлиять никак уже не могут.

Им банально - не отдаётся управление. И они не могут продолжать свою работу.

А ведь - они автоматические и никакой пользователь им не поможет.

Что же делать?

вторник, 26 ноября 2013 г.

Заметки о тестировании №2

Предыдущая серия "потока сознания была тут" - http://programmingmindstream.blogspot.ru/2013/11/blog-post.html

Продолжу.

Пока не забыл.

Опять же о "линейности" и "читаемости человеком".

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

Первое что приходит на ум это примерно следующее:

var
 SomeSettingValue : Value;
...
 SomeSettingValue := System.GetValue('SomeSetting');
 DoOurWork;
 System.SetValue('SomeSetting', SomeSettingValue );
- ну что сказать? А если тест упадёт? Всё? Система окажется в нестабильном состоянии? Ну "решение на поверхности" такое:
var
 SomeSettingValue : Value;
...
 SomeSettingValue := System.GetValue('SomeSetting');
 try
  System.SetValue('SomeSetting', SomeNewSettingValue);
  DoOurWork;
 finally
  System.SetValue('SomeSetting', SomeSettingValue );
 end;

- оно конечно лучше.

Но по-моему это то самый случай, когда "за деревьями скрывается лес".

Все эти try..finally явно не повышают читабельность.

Для человека, который должен мочь "читать тест" и "воспринимать его как тест-кейс".

"Хорошее" решение, как мне кажется выглядит примерно так:

 TemporaryChangedSystemStateFor('SomeSettingValue', SomeNewSettingValue, DoOurWork);

Ну или "по-русски":

 "Для шрифта системы в {(20px)}" "Построить предварительный просмотр и сравнить с эталоном" 

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

Но! С одной стороны мы обеспечиваем стабильность автоматических тестов, а с другой стороны мы не перегружаем человека читающего это лишней информацией.

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

А если он руками дальше не будет ничего тестировать? Зачем ему это?

А если будет, то там есть - "подсказка".

Но именно - подсказка, а не побуждение к действию.

Ну вот как-то так мне кажется.

Опять же - ни на чём - не настаиваю.


понедельник, 25 ноября 2013 г.

Заметки о тестировании

Ещё раз хочу повторить вот что:

1. тест должен быть линейным
2. похожим на тест-кейс
3. читаться человеком
4. оперировать терминами предметной области

К чему это я?

Я сегодня просматривал код. Много разного кода. И некоторые вещи заставили меня задуматься о том, что есть вещи которые видимо стоит повторять неоднократно.

Что я хотел сказать о TDD, но всё как-то недосуг. "Дорога в тысячу ли начинается с одного шага"

Моё знакомство с "промышленным тестированием" было описано тут - http://18delphi.blogspot.ru/2013/03/blog-post.html

И с тех пор я ничего лучшего не написал.

Но это было далеко не TDD.

И не "тестирование в широком смысле этого слова".