Wiki
Clone wikiMindStream / Статьи на русском / По мотивам GUIRunner часть 3.1
Сегодня дописал пост о том, как мы решили написать свой GUIRunner для FireMonkey. В комментарии к посту в одной из соцсетей Алексей Тимохин обратил мое внимание на другой известный фреймворк для тестирования — DUnitX. Я пытался найти альтернативу, использовать консольный вариант, но Александр был неумолим. Когда же зайдя в репозиторий я увидел готовый GUIRunner под FireMonkey, совсем поник.
Однако.
После первого запуска первым моим сообщением Александру было — " lol. Там «галочек» нету." Так что проблему решал не зря. После более внимательного изучения сложилось впечатление, что человек, который писал эту форму, тоже отчасти «поглядывал» в Original GUIRunner.
В целом я бы очень обрадовался такому подарку с месяц назад, пока шишки FireMonkey ещё не набились. Ну а сегодня мне было просто интересно, как решал эту же задачу другой программист.
Ряд ошибок мы допустили практически одинаковых. В посте я писал о том, как мы «связываем» тесты и ветки и в конце закончил предложением о рефакторинге с использованием TDictionary. Напомню, как в оригинале:
#!delphi l_Test.GUIObject := aNode.Items[l_Index]; ... l_TreeViewItem.Tag := FTests.Add(aTest);
Разработчик DUnitX поступил примерно также, правда, он сделали обертку над TTreeViewItem (в будущем добавлю к себе):
#!delphi type TTestNode = class(TTreeViewItem) strict private FFullName: String; FImage: TImage; public constructor Create(Owner: TComponent; Text: String; TestFullName: String); reintroduce; destructor Destroy; override; property FullName: String read FFullName; procedure SetResultType(resultType: TTestResultType); procedure Reload; end;
И связал каждый тест с веткой по имени теста.
#!delphi function TGUIXTestRunner.GetNode(FullName: String): TTreeViewItem; var i: Integer; begin Result := nil; i := 0; repeat begin if (TestTree.ItemByGlobalIndex(i) as TTestNode).FullName = FullName then Result := TestTree.ItemByGlobalIndex(i); Inc(i); end until Assigned(Result) or (i >= TestTree.GlobalCount); end;
Удивило меня другое:
#!delphi FFailedTests: TDictionary<String, ITestResult>;
Угадайте, зачем нам ключ String? Правильно, чтобы по нему добраться до ветки и сообщить о её состоянии после результата теста. Как по мне, перемудрили.
Отдельного упоминания заслуживает класс TTreeNode. Он в себе хранит «ссылку» на тест и картинку, которая будет изменять состояние ветки. Так как класс унаследован от TreeViewItem, такой код живет отлично:
#!delphi var testNode : TTreeViewItem; ... testNode := CreateNode(TestTree, test.Name, test.Fixture.FullName + '.' + test.Name); ... function TGUIXTestRunner.CreateNode(Owner: TComponent; Text: String; TestFullName: String): TTreeViewItem; begin Result := TTestNode.Create(Owner, Text, TestFullName); end; ... constructor TTestNode.Create(Owner: TComponent; Text, TestFullName: String); begin inherited Create(Owner); Self.Text := Text; FFullName := TestFullName; FImage := TImage.Create(Owner); FImage.Parent := Self; {$IFDEF DELPHI_XE6_UP} FImage.Align := TAlignLayout.Right; {$ELSE} FImage.Align := TAlignLayout.alRight; {$ENDIF} FImage.Bitmap.Create(15, 15); FImage.Bitmap.Clear(TAlphaColorRec.Gray); FImage.SendToBack; end;
В целом DUnitX произвёл на меня хорошее впечатление. Фреймворк кажется намного солиднее своего старшего брата. Интерфейсы и архитектуру ребята пересмотрели и, думаю, даже улучшили. Весь код очень аккуратный. Комментариев больше в разы. Буду присматриваться и сравнивать.
Ссылки
Репозиторий Delphi-Mocks. Необходим для компилирования фреймворка; Репозиторий DUnitX.
Updated