Wstęp
Test Complete to środowisko pełniące role w pełni funkcjonalnej platformy testowej. Pozwala na tworzenie testów automatycznych dla aplikacji webowych, mobilnych a także desktopowych. Platforma umożliwia pisanie testów automatycznych w postaci skryptowej, w której tworzymy nasze testy wykorzystując programowanie we wspieranych przez narzędzie językach programowania m.in. Python, JavaScript. Program umożliwia również tworzenie testów automatycznych w całości w sposób graficzny bez konieczności umiejętności programowania. Tworząc testy w ten sposób nakłada to na nasz framework pewne ograniczenia, uniemożliwiając integracje z narzędziami, które nie wchodzą we wspierany stack od firmy SmartBeara. W prawie, każdym przypadku optymalniejszym rozwiązaniem będzie budowa frameworku testowego opartego w całości na kodzie. Wprawdzie wybór takiego pojdejścia będzie droższe, z racji wymogu umiejętności programowania u testera, ale taki projekt będzie bardziej skalowalny oraz pozwoli na integrację z zewnętrznymi narzędziami, które nie są dostępne dla technologii TestComplete.
Tworzenie nowego projektu
Aby poznać możliwości jakie oferuje TestComplete stworzony zostanie minimalistyczny framework, zawierające testy automatyczne wbudowanej w system operacyjny Windows, aplikacji kalkulatora. Aby stworzyć nowy projekt uruchamiamy aplikacje TestComplete. Powita nas okno startowe:
(Rys. 1.0) - Główne okno programu TestComplete.
Aby rozpocząć tworzenie nowego projektu klikamy przycisk umieszczony na stronie startowej o nazwie NEW PROJECT
lub z paska toolbar
wybieramy kolejno: File -> New -> Project
. Jeśli zrobiliśmy wszystko wg. powyższych instrukcji powinno pojawić się okno tworzenia nowego projektu.
(Rys. 1.1) - Okno tworzenia projektu w TC.
Wpisujemy nazwę projektu, u nas będzie to WinCalculatorTests
. Dodatkowo możemy wybrać lokalizację w jakiej zapisany zostanie nasz framework (formularz Project location
), którą pozostawimy jako domyślną. Bardzo ważną rzeczą będzie wybór języka programowania na bazie, którego będziemy tworzyć nasz framework testów automatycznych. Ustawiamy formularz Scripting language
na wartość Python
. Po opisanych krokach klikamy na przycisk Finish.
Po chwili oczekiwania na ekranie pojawi nam się okno nowego projektu.
(Rys. 1.3) - Interfejs TC po utworzeniu pustego projektu.
Interfejs środowiska TestComplete'a można podzielić na 3 główne części:
Workspace
- główna przestrzeń robocza, na której m.in. edytujemy nowe testy w postaci kodu, bądź wybieramy kroki przy wyborze graficznej metody pisania testów. To tutaj projektujemy również nasze suite'y testowe oraz przeglądamy logi i wyniki po uruchomieniu testów.
Project Explorer
- Obszar zawierający wszystkie pliki, jakie zawarte są w naszym projekcie. Dodajemy tu nowe pliki skryptowe, dane testowe, lub pliki innego typu potrzebne do wykonywania testów.
Toolbar
- Górny pasek z funkcjami, które mogą się zmieniać w zależności od trybu w jakim znajduję się aokurat nasz program. Zawiera m.in. takie funkcjonalności jak obsługa debuggura czy funkcjonalność mapowania kontrolek testowanej aplikacji.
Kiedy tworzymy nowy projekt w TestComplete, program domyślnie tworzy strukturę frameworku, gdzie zawarte są elementy pozwalające na pisanie testów w sposób graficznych tzw. KeywordTests
, patrz _(Rys.1.3)_. Aby z tego zrezygnować należy usunąć element KeywordTests
z obszaru Project Explorer'a naszego projektu. W tym celu należy najechać kursorem na wymieniony element, a następnie klikając na niego prawym klawiszem myszki wybrać Remove
.
Tworzenie pierwszego testu
Do pisania nowych modułów pythonowych nie jest wymagana osobna instalacja Python'a. TestComplete ma swój własny interpreter tego języka. Spowodowane to jest tym, że oprócz standardowych instrukcji pythonowych TestComplete posiada, również swój własny zestaw instrukcji/funkcji/słów kluczowych, których standardowy Python nie będzie w stanie zinterpretować. Jako przykład można tu wskazać wbudowane funkcje służące do logowania poszczególnych kroków testów. Więcej pod linkiem.
Aby napisać pierwszy test w TestComplete używając do tego Python'a należy dwa razy kliknąć na nowo powstały plik Script/Unit1
, co otworzy nam edytor pozwalający pisać kod naszego testu. Z racji, że cały nasz framework tworzymy w oparciu o Pythona będziemy korzystać ze Style Guide'a [PEP-8], który zawiera reguły dotyczące nazewnictwa oraz pisania poprawnie sformatowanego kodu. Zmieńmy nazwę naszego pliku, na poprawną wg. konwencji PEP-8, klikając prawy przyciskiem myszy na plik Unit1
i wybierając opcję rename
oraz wpisując nową nazwę pliku. Unit1 -> test_win_calculator
.
Poniżej stworzona została pierwsza funkcja testowa, którą należy przepisać do utworzonego edytora.
def test_hello_test_complete():
Log.Message("Hello from first test written in TestComplete")
W celu uruchomienia powyższego kodu testu, należy kliknąć prawy przyciskiem myszy na nazwę funkcji oraz wybrać Run This Routine
. Po uruchomieniu napisanego testu, w naszym WorkSpace'ie
otworzy się okno z wynikiem i logami naszego skryptu
(Rys. 2.1) - Widok prezentujący wynik oraz dodatkowe informacje o wykonanym teście.
W podsumowaniu naszego testu możemy wyczytać takie informacje, jak wynik naszego testu, czas uruchomienia oraz logi, które zostały zawarte w ciele naszej funkcji testowej.
Pierwszym krokiem testu automatycznego kalkulatora, będzie uruchomienie aplikacji. Najprostszym sposobem na obsłużenie tego wymagania, będzie skorzystanie z wbudowanego mechanizmu przechowywania testowanych aplikacji w TestComplete (TestedApps
)
Dodanie aplikacji do TestedApps
można wykonać na kilka sposobów. Jednym z nich będzie uruchomienie kalkulatora ręcznie, a następnie wykorzystanie TestComplete'owego ObjectBrowsera
. Jest to wbudowana w środowisko funkcjonalność pozwalająca na podglądanie aktualnie działających procesów. Można nazwać to SmartBear'owym Process Explorerem. Oprócz samego widoku aktualnie uruchomionych aplikacji ObjectBrowser daje nam możliwość poglądu w głąb strukturę graficzną aplikacji czyli jej kontrolek oraz elementów.
Aby dodać kalkulator do testowanych aplikacji wyszukujemy go na liście procesów w ObjectBrowserze, klikamy prawym przyciskiem myszy i wybieramy Add Process to Tested Apps
, potwierdzamy wybór klikając Yes
. Możemy zauważyć, że na liście elementów projektu pojawił nam się nowy item o nazwie Tested Apps
.
(Rys. 3.1) - Lista elementów w projekcie po dodaniu aplikacji kalkulatora.
Napiszmy więc nowy test, który rozpocznie się od uruchomienia aplikacji kalkulatora. Stwórzmy pomocniczą funkcję start_calculator
, którą będziemy mogli reużywać w kolejnych testach.
def start_calculator():
TestedApps.WindowsCalculator.Run()
Log.Checkpoint("Calc.exe started.")
def test_calculator_run():
start_calculator()
Uruchamiając test test_calculator_run
uruchomimy tym samym aplikację kalkulatora oraz otrzymamy następujące podsumowanie testu.
(Rys. 3.2) - Wynik testu uruchamiającego kalkulator.
Mapowanie kontrolek - Object Spy
Automatyzując aplikacje desktopowe podobnie jak przy aplikacjach webowych, aby wejść w interakcje z jakąś kontrolką aplikacji należy mieć o niej jakąś informację. TestComplete posiada wbudowany mechanizm o nazwie ObjectSpy
, który pozwala dobrać się do atrybutów wybranego elementu aplikacji desktopowej.
Z paska Toolbar
wybieramy zaznaczoną na poniższym zdjęciu ikonę, która uruchamia nam narzędzie ObjectSpy
(Rys. 3.3) - Przycisk uruchamiający ObjectSpy.
Uruchamia się okno przedstawione na poniższym zdjęciu:
Mając uruchomioną testowaną aplikację oraz ObjectSpy'a, przeciągamy zaznaczoną na _Rys. 3.4_ ikonkę na wybrany element aplikacji kalkulatora. Po umieszczeniu kursora na wybranym elemencie aplikacji, element ten powinien podświetlić się na czerwono.
(Rys. 3.5) - Kalkulator podczas mapowania kontrolek.
Po puszczeniu przycisku myszki w oknie ObjectSpy pojawią się informacje dotyczące wybranej kontrolki aplikacji kalkulatora takie jak pełna ścieżka, przez którą możemy wejść w interakcje z zaznaczoną kontrolką z poziomu kodu oraz dostępne metody i atrybuty TestComplete'a, które możemy wywołać na wybranej kontrolce.
(Rys. 3.6) - Zmapowane gówne okno aplikacji kalkulatora.
Dodajmy 3 kolejne kontrolki potrzebne do zautomatyzowania scenariusza dodawania 2+2
. Potrzebujemy do tego klawiszy 2
, +
, =
oraz okienko w którym wypisywany jest wynik działania.
(Rys. 3.6) - Kontrolka, w której pokazywany jest wynik operacji na kalkulatorze.
Każdą ze zmapowanych ścieżek umieścimy w osobnych funkcjach. Zwiększy to czytelność oraz reużywalność kodu.
def get_two_calc_key():
return NameMapping.Sys.Process("Microsoft.WindowsCalculator").UIAObject("Kalkulator").UIAObject("LandmarkTarget").UIAObject("Klawiatura_numeryczna").UIAObject("Dwa")
def get_plus_calc_key():
return NameMapping.Sys.Process("Microsoft.WindowsCalculator").UIAObject("Kalkulator").UIAObject("LandmarkTarget").UIAObject("Operatory_standardowe").UIAObject("Plus")
def get_equals_calc_key():
return NameMapping.Sys.Process("Microsoft.WindowsCalculator").UIAObject("Kalkulator").UIAObject("LandmarkTarget").UIAObject("Operatory_standardowe").UIAObject("Równa_się")
def get_calc_result_form():
return NameMapping.Sys.Process("Microsoft.WindowsCalculator").UIAObject("Kalkulator").UIAObject("LandmarkTarget").UIAObject("Wyświetlana_wartość_to_0").UIAObject("TextContainer").UIAObject("NormalOutput")
Mając dostęp do powyższych kontrolek stworzymy test sprawdzający poprawność działania dodawania posiłkując się wbudowanymi obiektami TestComplete pozwalającymi na wybraną interakcję z kontrolkami:
- Click() - imituje kliknięcie lewym klawiszem myszy w wybraną kontrolkę
- Text - atrybut zwracający wartość tekstową zapisaną w danej kontrolce
def test_calcultor_sum():
start_calculator()
two_key = get_two_calc_key()
two_key.Click()
plus_key = get_plus_calc_key()
plus_key.Click()
two_key = get_two_calc_key()
two_key.Click()
equals_key = get_equals_calc_key()
equals.Click()
result = get_calc_result_form().Text
assert result == "4"
Ścieżka mapująca kontrolkę wyniku działania ma charakter dynamiczny to znaczy, że jej wartość jest zależna od przechowywanej w niej wartości, fragment ścieżki: UIAObject("Wyświetlana_wartość_to_0")
. Żeby zoptymalizować nasz kod zmieńmy ciało funkcji get_calc_result_form().
def get_calc_result_form(expected_result_value: str):
return NameMapping.Sys.Process("Microsoft.WindowsCalculator").UIAObject("Kalkulator").UIAObject("LandmarkTarget").UIAObject(f"Wyświetlana_wartość_to_{expected_result_value}").UIAObject("TextContainer").UIAObject("NormalOutput")
Kod testu po zmianach:
def test_calcultor_sum():
start_calculator()
two_key = get_two_calc_key()
two_key.Click()
plus_key = get_plus_calc_key()
plus_key.Click()
two_key = get_two_calc_key()
two_key.Click()
equals_key = get_equals_calc_key()
equals.Click()
exp_result = "4"
result = get_calc_result_form(exp_result).Text
assert result == exp_result
Co dalej?
Platforma testowa od SmartBear'a to potężne narzędzie, której wszystkie funkcjonalności nie sposób zawrzeć w pojedynczym artykule. Poniżej wypisałem kolejne kroki opisujące potencjalny rozwój naszego frameworku testowego.
- Implementacja wzorca projektowego Page Object Pattern w oparciu o programowanie obiektowe lub podejście funkcyjne.
- Przechowywanie zmapowanych kontrolek w strukturze TestComplete'a [NameMapping].
- Dopisanie nowych testów end 2 end sprawdzających wszystkich funkcjonalności kalkulatora.
TestComplete nie jest rozwiązaniem open source'owym, ale wyróżnia się bardzo dobrym Community, na którym można uzyskać odpowiedź na pytanie czysto techniczno-projektowe.
Listing kodu po zmianach:
def test_hello_world():
Log.Message(f"Hello from first test written in TestComplete")
def start_calculator():
TestedApps.WindowsCalculator.Run()
Log.Checkpoint("Calc.exe started.")
def get_two_calc_key():
return NameMapping.Sys.Process("Microsoft.WindowsCalculator").UIAObject("Kalkulator").UIAObject("LandmarkTarget").UIAObject("Klawiatura_numeryczna").UIAObject("Dwa")
def get_plus_calc_key():
return NameMapping.Sys.Process("Microsoft.WindowsCalculator").UIAObject("Kalkulator").UIAObject("LandmarkTarget").UIAObject("Operatory_standardowe").UIAObject("Plus")
def get_equals_calc_key():
return NameMapping.Sys.Process("Microsoft.WindowsCalculator").UIAObject("Kalkulator").UIAObject("LandmarkTarget").UIAObject("Operatory_standardowe").UIAObject("Równa_się")
def get_calc_result_form(expected_result_value: str):
return NameMapping.Sys.Process("Microsoft.WindowsCalculator").UIAObject("Kalkulator").UIAObject("LandmarkTarget").UIAObject(f"Wyświetlana_wartość_to_{expected_result_value}").UIAObject("TextContainer").UIAObject("NormalOutput")
def test_calculator_run():
start_calculator()
def test_calcultor_sum():
start_calculator()
two_key = get_two_calc_key()
two_key.Click()
plus_key = get_plus_calc_key()
plus_key.Click()
two_key = get_two_calc_key()
two_key.Click()
equals_key = get_equals_calc_key()
equals.Click()
exp_result = "4"
result = get_calc_result_form(exp_result).Text
assert result == exp_result