Silverlight – koncepcja logowania z użyciem MembershipProviders oraz WCF RIA cz.2

W poprzednim wpisie przedstawiłem w jaki sposób zaimplementować mechanizm logowania po stronie serwera. Wykorzystałem do tego celu WCF RIA oraz znane z ASP MembershipProvidery. Tym razem przedstawię jak wymusić logowanie po stronie klienta, oraz w jaki sposób można dynamicznie zmieniać providerów, którzy walidują usera.
Poprzednim razem stworzyliśmy już szkielet aplikacji, zatem mamy projekt klienta oraz projekt serwera. Zacznijmy od “włączenia” FormsAuthentication po stronie klienta. W pliku App.xaml.cs , w konstruktorze dorzućmy następujące linijki

Następnie zanim pokażemy treść naszej strony, musimy sprawdzić czy użytkownik jest zalogowany.Jeżeli nie, należy pokazać okno logowania. Możemy to zrobić w następujący sposób.

WebContext jest to klasa generowana automatycznie przy buildowaniu solucji.Jej kod powinniśmy odnaleźć w katalogu Generated_Code po stronie klienta. Klasa ta posiada właściwość Current, która zwraca obecny context zarejestrowany w kolekcji ApplicationLifetimeObjects.
Okno logowania zostało zaimplementowane jako ChildWindow. Jego kod może wyglądać w następujący sposób

Kontrolka logowania jest bardzo prosta, składa się ona z dwóch labelek, textboxa oraz passwordBoxa. Powyższy kod powinien wygenerować mniej więcej takie oto okno
WCF RIA
Mechanizm wywoływania operacji logowania znajduje się natomiast w ViewModelu kontrolki logowania. Po naciśnięciu przycisku OK, dzięki zastosowaniu DelegateCommand uruchamiany następującą funkcję

W funkcji tej najpierw sprawdzamy, czy użytkownik podał dane do logowania, a następnie wywołujemy kolejną funkcję PerfomrLoginOperation. Przedstawia się w następujący sposób

Widzimy zatem, że wywołujemy funkcję logowania z serwisu WCF, w parametrach natomiast podajemy login oraz hasło przechwycone z okna. Dodatkowo w parametrze podajemy delegata do funkcji, która zostanie wykonana po zakończeniu operacji logowania.

Jeżeli odpowiedź z serwisu WCF zawiera błędy to wiemy, że operacja logowania się nie powiodła. Wyświetlamy zatem stosowny komunikat. Jeżeli natomiast wszystko jest Ok, pole loginOperation.Error będzie miało wartość null. Możemy wtedy pokazać naszą wiadomość dostępną jedynie dla zalogowanych użytkowników.
Mamy zatem ochronę przeciwko nieautoryzowanemu dostępowi do klienta. Jednak co w przypadku gdy nasz klient musi w calach np. zassania danych pobrać je przy wykorzystaniu jakiegoś WebServicu. Na ogól wystawiany jest wtedy drugi WebService do pobierania danych z jakiejś bazy. Do takiego WebServicu w teorii może dostać się każdy – my natomiast chcemy żeby dane można było pobierać jedynie po zalogowaniu się do aplikacji. Na szczęście nasz dodatkowy WebServise można w prosty sposób zabezpieczyć przed dostępem osób trzecich. Stwórzmy zatem sobie nowy WebService służący do pobierania danych. Do projektu Webowego dodajmy nową klasę typu “Domain Service” oraz dopiszmy do nie jedną metodę symulującą pobieranie danych.

Do takiego Servicu może dostać się każdy. Jeżeli natomiast naszą klasę udekorujemy atrybutem [RequiresAuthentication()] tylko użytkownicy, którzy pomyślnie przeszli autentykację przez nasz CustomAuthenticationService będą w stanie pobrać dane. W przeciwnym wypadku zostanie rzucony wyjątek.
debugger

Zastanówmy się teraz w jaki sposób można byłoby logować się do naszej aplikacji np. przy pomocy konta Google. Zacznijmy od tego, że musimy stworzyć własny MembershipProvider. W tym celu dodajmy do projektu Webowego nową klasę “GoogleMembershipProvider“, która będzie rozszerzać klasę MembershipProvider. Klasa MembershipProvider jest klasą abstrakcyjną zatem musimy zoverridować wszystkie jej funkcje (nie musimy wrzucać tam logiki, narazie po prostu wpiszmy tam cokolwiek aby przeszła kompilacja). Mając stworzony szkielet providera musimy go dorzucić do listy wszystkich providerów (tak jak to zrobiliśmy z SqlMembershipProvider-em). Modyfikujemy zatem sekcję podsekcję providers z sekcji membership na następującą.

Mając w kolekcji wszystkie nasze providery pozostaje nam się zastanowić, w jaki sposób raz używać GoogleMembershpProvidera, a innym razem SqlMembershipProvidera. Ja zrobiłem to w następujący sposób. Najpierw utworzyłem enuma, w którym przechowuje “nazwy” moich providerów

Następnie wykorzystałem parametr customData interfejsu IAuthentication. Po stronie klienta w funkcji logowania po prostu przesyłam typ konta do którego się loguję. Wygląda to w ten sposób:

Następnie po stronie serwera wybieram tego providera, przez którego loguje się użytkownik.

Zauważmy, że linijka

została zastąpiona przez

zatem teraz mamy wpływ na to, którego MembershipProvidera użyjemy. Jeżeli chodzi o samo sprawdzenie czy dany użytkownik podał poprawne dane do konta google, to sprawa jest trochę kłopotliwa(tak mi się wydaje :D). Nie udało mi się użyć biblioteki dotNetOpenAuth, gdyż z tego co widzę logowanie poprzez tą bibliotekę wymaga przekierowania na stronę Googla. Niestety taka operacja nie jest dozwolona w Silverlighcie (Silverlight nie wspiera cross-domain policy). Ponadto wykorzystanie tej biblioteki po stronie serwera również nie przyniosło oczekiwanych rezultatów. Ostatecznie zatem skorzystałem z opisu znajdującego się na stronie Googla http://code.google.com/intl/pl-PL/apis/accounts/docs/AuthForInstalledApps.html#Using. Czyli po prostu spreparowałem odpowiedni HTTPS Post Request. Funkcja ValidateUser z GoogleMembershipProvidera wygląda zatem następująco

Ciężko mi jednak stwierdzić czy takie logowanie jest bezpieczne. Co prawda nie udało mi się wyłapać nic konkretnego poprzez Wiresharka lub Fiddlera, jednakże ekspertem od zabezpieczeń niestety nie jestem.
W następnym wpisie postaram się króciutko przedstawić bibliotekę Microsoft Enterprise Library, a właściwie jeden z jej bloków – Security Block.Wykorzystam go do zezwalania użytkownikom do dostępu do funkcji serwisu, w zależności od ich roli w systemie.

Silverlight – koncepcja logowania z użyciem MembershipProviders oraz WCF RIA cz.2

Silverlight – koncepcja logowania z użyciem MembershipProviders oraz WCF RIA cz.1

W dzisiejszym wpisie postaram się przedstawić moją koncepcję logowania do aplikacji Silverlightowej z wykorzystaniem MembershipProviders oraz własnego AuthenticationService.
Zacznijmy od stworzenia bazy danych, w której będziemy przetrzymywać informacje o użytkownikach – hasła, role itp. Bazę danych stworzymy przy użyciu narzędzia aspnet_regsql.exe, które to wygeneruje schemat bazy danych dostosowany do możliwości SqlMembershipProvider-a. aspnet_regsql.exe znajduje się w katalogu C:WINDOWS\Microsoft.NET\Framework\wersjaFrameworka\aspnet_regsql.exe. Czyli w moim przypadku jest to C:WINDOWS\Microsoft.NET\Frameworkv\4.0.30319\aspnet_regsql.exe.
regsql
Klikając podwójnie na ikonę aplikacji pojawia nam się następujące okno
wizzard
Klikamy w przycisk “Dalej”. W kolejnym oknie mamy do wyboru dwie opcje

  • Configure SQL Server for application services
  • Remove application services from an existing database

Pierwsza opcja odpowiedzialna jest za stworzenie struktury danych do naszego sytemu logowania, natomiast druga usuwa z bazy danych tabele służące do logowania itp. Wybieramy opcję bramkę nr 1. W kolejnym oknie musimy podać connection string do naszego serwera bazy danych oraz login i hasło. W moim przypadku wygląda to w następujący sposób:

wizzard2

Jeżeli wszystko pójdzie OK pokaże się nam następujące okno, w którym to możemy zobaczyć, że została stworzona baz danych aspnetdb

wizzard3

Ostatecznie klikamy na przycisk “Next”,a następnie w “Finish” aby dokończyć działanie kreatora

wizzard4

Mając gotową bazę danych możemy przystąpić do właściwego kodowania. Zacznijmy od stworzenia nowego projektu typu “Silverlight Application”

silverlightapp
po kliknięciu przycisku “OK” włączamy WCF RIA Service
EnableWCF

Mając stworzony szkielet aplikacji możemy zabrać się za napisanie własnego AuthenticationService. Do projektu webowego (nazwaprojektu.Web) dodajmy nowy element typu “Domain Service”. PPM na projekt, następnie “Add new item”, w oknie które się otworzy wybieramy “Domain Service”. Nadajemu mu nazwę CustomAuthenticationService.cs

AddItemSmall

DomainServiceClass

Nasza właśnie dodana klasa powinna wyglądać mniej więcej w taki sposób

W następnym kroku zaimplementujmy w klasie CustomAuthenticationService interfejs
IAuthentication where T: IUser. Interfejs ten wygląda w następujący sposób:

Widzimy zatem, że zanim go zaimplementujemy musimy posiadać obiekt implementujący inny interfejs – IUser. Przystąpmy zatem do stworzenia naszej klasy modelującej użytkownika – nazwijmy ją UserDTO.Stwórzmy nowy projekt typu “Class library”. W projekcie tym stwórzmy klasę UserDTO, która będzie dziedziczyła po klasie UserBase znajdującej się w przestrzeni nazw System.ServiceModel.DomainServices.Server.ApplicationServices

Mając gotową klasę reprezentującą użytkownika zaimplementujmy w końcu w klasie CustomAuthenticationService interfejs IAuthentication. Po implementacji interfejsu nasz serwis wygląda w następujący sposób:

Pozostaje nam “tylko” zaimplementować odpowiednie metody. Funkcja która nas najbardziej interesuje to oczywiście funkcja logowania. Przy jej implementacji posłuży się mechanizmem znanym z ASP mianowicie z MembershipProviders. Klasa MembershipProvider jest to abstrakcyjna klasa posiadająca szereg funkcji mającej na celu walidowanie poprawnego użytkownika, rejestrację itp. Stwórzmy zatem własną klasę providera dziedziczącą po klasie MembershipProvicer. .NET dostarcza nam domyślny provider, którym jest SqlMembershipProvider, jest on napisany w taki sposób, aby mógł porozumiewać się z bazą, którą stworzyliśmy na początku. Aby wykorzystać wspomnianego wcześniej providera musimy odpowiednio zmodyfikować plik Web.config. Po pierwsze dodajemy do niego ConnectionString do naszej bazy danych, w moim przypadku będzie to wyglądało w następujący sposób

Następnie musimy “pokazać” naszej aplikacji, że będziemy używać FormsAuthentication oraz MembershipProvider-ow. Dorzućmy zatem do config następujące rzeczy:(w sekcji system.web)

następnie dorzucamy do kolekcji providerów SqlMembershipProvider

We wpisach tych dorzuciliśmy możliwość autentykacji poprzez providery, ustawiliśmy SqlMembershipProvider jako domyślny provider, oraz dorzuciliśmy go do listy wszystkich providerów. W celu sprawdzenia czy nasza konfiguracja jest poprawna możemy posłużyć się mechanizmem dostarczonym nam przez Visual Studio. Klikamy “Projekt”, a następnie ASP.NET Configuration
ASPConfiguration
Zostaniemy przeniesieni do domyślnej przeglądarki internetowej, a naszym oczom ukaże się następująca strona.
AdminTool
Z poziomu tej stronki możemy dodawać użytkowników, przydzielać im role itp, dzięki czemu możemy szybko sprawdzić czy podstawowe funkcje naszego providera działają, oraz czy nasza konfiguracja została przeprowadzona prawidłowo. Wracając natomiast do naszego systemu logowania, pozostało nam jedynie odpowiednio zmodyfikować funkcję odpowiedzialną za logowanie.Robimy to w następujący sposób:

W funkcji ValidateCredentials odbywa się sprawdzanie czy użytkownik podał właściwe hasło oraz login. Do walidacji wykorzystujemy dodany wcześniej SqlMembershipProvider – właściwość Membership.Provider zawsze zwraca obiekt domyślnie zdefiniowanego providera. W przypadku gdy walidacja się powiedzie tworzymy obiekt użytkownika, a następnie tworzymy i szyfrujemy FormsAuthenticationTicket oraz tworzymy ciasteczko (cookie), które przesyłamy w responsie wysyłanym przez serwer do klienta. Mechanizm logowania jest już prawie gotowy. Musimy jedynie zaimplementować jeszcze pozostałe funkcje interfejsu IAuthentication.Nie ma raczej w nich nic trudnego

Funkcja Logout po prostu wywołuje funkcję SingOut z klasy FormsAuthentication. Natomiast funkcja GetUser zwraca zalogowanego użytkownika. W funkcji tej najpierw sprawdzamy czy użytkownik jest zalogowany (do tego celu używamy zmiennej IsAuthenticated), a następnie sprawdzamy czy przypadkiem jego sesja nie wygasła. W przypadku gdy wszystko jest OK zwracamy obiekt typu UserDTO,w przeciwnym razie zwracamy null.

Podsumowując, udało nam się stworzyć mechanizm logowania. Jak na razie gotowa jest strona serwera.
W następnym wpisie pokażę w jaki sposób należy wywołać logowanie od strony klienta. Przedstawię również pomysł w jaki sposób zmieniać providerów przez które użytkownik ma się logować. Może to być przydatne jeżeli chcielibyśmy logować się np. poprzez inne serwisy jak Google, Facebook itp.

Silverlight – koncepcja logowania z użyciem MembershipProviders oraz WCF RIA cz.1

Coded UI Test – tworzenie testów UI przy pomocy Visual Studio Ultimate

Poszukując sposobów automatycznego testowania aplikacji natrafiłem na ciekawą funkcjonalność Visual Studio 2010 Ultimate. Mianowicie w tej wersji naszego ulubionego IDE znalazło się miejsce na nowy typ testów – Coded UI Test. Coded UI Test jest to automatyczny test UI, który tworzymy poprzez nagrywanie akcji jakie wykonujemy w naszej aplikacji. Nie musimy pisać ani jednej linijki kodu żeby przetestować jakąś funkcjonalność naszego programu. Jedyne co musimy zrobić jest to przejście przez wszystkie niezbędne kroki w naszej aplikacji, tak aby zweryfikować naszą funkcjonalność. W celu stworzenia Coded UI Test musimy po pierwsze zaopatrzyć się w wersję Ultimate Visual Studio 2010. Z tego co wiem, niestety niższe wersje nie mają funkcjonalności automatycznego testowania. Następnie musimy stworzyć nowy Test Project. W tym celu z menu File wybieramy New a następnie Project. W oknie, które się pokaże, w drzewie po lewej stronie wybieramy Test, a następnie w prawej części okna Test Project
Project
Mając stworzony projekt testowy dodajemy do niego nowy Coded UI Test. W Solution Explore-rze klikamy PPM na projekt testowy, a następnie z menu contextowego wybieramy Coded UI Test.
CodedUITest
W oknie, które się pojawi wybieramy Record actions,edit UI map or add assertion lub gdy mamy już gotowe testy możemy wybrać opcję nr 2. Po wybraniu pierwszej opcji okno Visual Studio zostanie zminimalizowane, a w prawym dolnym rogu pulpitu ukaże się następujące okienko.
Record
Służy ono do nagrywania testu. Naciskając czerwony przycisk rozpoczynamy proces nagrywania testu. W moim przypadku będę testować aplikację, którą testowałem również w poprzednim poście. Wykonuje zatem czynności mające na celu sprawdzenie funkcjonalności dodawania liczb. Po kliknięciu na textbox-a pojawia się nad nim dymek,że dana akcja jest nagrywana.
UI
Po "wyklikaniu" całej ścieżki mającej sprawdzić funkcjonalność klikamy przycisk "Generate code”" (ten najbardziej z prawej strony). Następnie w okienku, które się pojawi
MethodName
podajemy nazwę naszego testu. Po kliknięciu OK, Visual Studio wygeneruje nam kod odpowiedzialny za nasz test.
SourceUIMap
W celu odpalenia naszego testu z menu Test wybieramy Run, a następnie Tests in Current Context. Visual Studio przejdzie teraz wszystkie kroki, które zostały nagrane w teście. Ok, niby wszystko ładnie pięknie, ale tak naprawdę nic nie sprawdziliśmy. Jedyne co zrobiliśmy to wykonaliśmy pewne operacje na naszej aplikacji, jednakże nie zweryfikowaliśmy danych otrzymanych w wyniku operacji dodaj. W celu dodania warunku sprawdzającego do naszego testu, musimy dodać tzw. asercję. Aby to zrobić klikamy PPM na wolne pole edytora, pod funkcją this.UIMap.RecordedMethod1() (ta funkcja została wygenerowana podczas nagrywania testu). Następnie z menu contextowego wybieramy
Generate
Otwiera nam się znane już wcześniej okienko nagrywania testu. Jednakże tym razem naciśnijmy przycisk celownika i przeciągnijmy go na textbox-a, w którym ma pojawić się wynik dodawania
AutomationAdding
Nasz textbox zostanie obramowany, natomiast w prawym dolnym rogu pulpitu pojawi się nowe okno pokazujące propertisy naszego przycisku. Wybierzmy właściwość Text ,a następnie naciśnijmy przycisk Add Assertion
Assertion
W oknie, które się pojawi możemy wybrać warunek, który musi spełnić dana właściwość (w tym przypadku Text), aby test przeszedł. Ustawmy tam wartość np. 3. Następnie klikamy OK oraz nadajemy nazwę naszej assercji.Po wykonaniu tych czynności w kodzie naszego pojawi się dodatkowa linijka
AddAssertion
Teraz za każdym razem gdy odpalimy test na samym jego końcu będzie sprawdzana nasza assercja. W przypadku gdy wartość właściwości Text nie będzie równa 3 nasz test nie przejdzie.
Coded UI Test

Coded UI Test – tworzenie testów UI przy pomocy Visual Studio Ultimate

UI Automation – czyli testy automatyczne w .NET

Biblioteka Microsoft UI Automation ujrzała światło dzienne wraz z premierą .NET 3.0 – jednakże pozostała ona w cieniu swoich większych braci WPF oraz WPF, które również zostały wprowadzone do Frameworka 3.0. Microsoft UI automation zapewnia nam dostęp do wszystkich elementów drzewa wizualnego aplikacji. Dzięki czemu mamy możliwość:

  • Znajdowania wybranych przez nas kontrolek
  • Interakcji z kontrolkami – wpisywanie tekstów do TextBox-ów, klikanie w przyciski itp
  • Wczytywania wartości już wprowadzonych do kontrolek

Każda kontrolka znajdująca się na widoku jest traktowana przez UI Automation jako AutomationElement. Rootem naszego drzewa wizualnego pulpit, dostęp do niego uzyskujemy poprzez statyczną właściwość

Mając dostęp do roota możemy następnie poruszać się po drzewie tak aby znaleźć interesujące nas elementy. Załóżmy, że napisaliśmy prostą aplikację WPF-ową, która wykonuje podstawowe obliczenia matematyczne (pomysł na prostą aplikację do testowania zaczerpnięty z tej strony – kod jednak pisałem po swojemu :D).
Wizualnie aplikacja ta przedstawia się w następujący sposób:

W celu poruszania się po drzewie wizualnym możemy skorzystać z dwóch metod

pierwsza z nich zwraca nam konkretny element, który spełnia podane przez nas kryterium. Pierwszy parametr funkcji TreeScope scope określa nam sposób przeszukiwania drzewa wizualnego. Dostępne opcje to:

  • Element – wyszukiwanie odbywa się tylko na danym elemencie
  • Children – wyszukiwanie odbywa się na wszystkich dzieciach danego elementu – nie zagłębiamy się rekurencyjnie,sprawdzamy tylko jeden poziom w dół od danego elementu
  • Descendants – wyszukiwanie odbywa się na wszystkich potomkach – przeszukiwanie rekurencyjne w dół
  • Subtree – wyszukiwanie odbywa się na wszystkich potomkach, oraz elemencie od którego zaczynamy
  • Ancestors – wyszukiwanie odbywa się na wszystkich rodzicach – rekurencyjnie w górę drzewa

Drugim z parametrów jakie musimy podać jest to UIAutomation.Condition. Parametr ten określa jaki warunek musi spełnić dany element aby został uwzględniony w wyniku wyszukiwania. Sama klasa Condition jest klasą abstrakcyjną, z której dziedziczą wyspecjalizowane klasy. Do określenia warunków najczęściej będziemy korzystać z potomka klasy Condition, mianowicie z

określa ona jaka właściwość kontrolki/aplikacji będzie uwzględniana przy wyszukiwaniu wyniku.

Pierwszym krokiem przy testowaniu aplikacji będzie oczywiście znalezienie głównego okna programu. Mając w solucji stworzony projekt z aplikacją WPF-ową, dodajmy drugi projekt – tym razem będzie to projekt typu ConsoleApplication.Następnie musimy dodać referencję do odpowiednich ddl-ek. Potrzebujemy następujących bibliotek:

  • UIAutomation.dll
  • UIAutomationClient.dll
  • UIAutomationType.dll

Następnie w naszym projekcie konsolowym w metodzie main odpalamy aplikację, którą chcemy testować. W moim przypadku wygląda to w następujący sposób:

Następnie musimy dostać się do głównego okna testowanej aplikacji. Możemy to zrobić używając funkcji FindFirst:

lub możemy uzyskać dostęp do okna przy pomocy funkcji

W pierwszym przypadku wykorzystując funkcję FindFirst przeszukujemy wszystkie dzieci (TreeScope.Children) roota (czyli pulpitu). Jako warunek wyszukiwania podaliśmy obiekt klasy PropertyCondition. Jako właściwość do porównywania podaliśmy id processu (AutomationElement.ProcessIdProperty). W drugim przypadku po prostu uzyskujemy dostęp do okna dzięki znajomości jego uchwytu (właściwość MainWindowHandle z klasy Process). Według mnie lepiej skorzystać z funkcji pierwszej, zwłaszcza gdy odpalamy aplikację, która się długo uruchamia. W pierwszym przypadku wystarczy zrobić odpowiednią funkcję oczekującą na odpalanie (gdyż funkcja zwróci nam null gdy okna jeszcze nie będzie), natomiast druga opcja rzuci nam wyjątek. Oczekiwania na załadowanie okna może wyglądać w następujący sposób

Mając dostęp do głównego okna możemy przeprowadzić testy – przetestujemy czy po wpisaniu danych do tekstboksów i przeprowadzeniu odpowiednich akcji (dodawanie,usuwanie,odejmowanie,dzielenie) w polu wynik pojawi się odpowiednia wartość. Zacznijmy od zlokalizowania textboxów. Posłużmy się tutaj przedstawioną wcześniej funkcją FindFirst.

Wyszukiwanie odbywa się po właściwości AutomationElement.AutomationIdProperty – właściwość ta jest zawsze taka sama jak nazwa kontrolki – chyba, że ktoś ją zmieni przy pomocy AttachedProperty AutomationProperties.AutomationId. Odpalając powyższy kod okazuje się, że nasza zmienna firstTextbox jest nullem – czyli framework nie był w stanie znaleźć naszego textboxa. Dlaczego tak się stało?? Odpowiedzi może nam dostarczyć aplikacja Snoop, dzięki której możemy podejrzeć drzewo wizualne naszego programu.
snoopResult
Na powyższym screenie widzimy, że szukany textbox nie jest bezpośrednim dzieckiem MainWindow, jest natomiast dzieckiem grida (tego można było się spodziewać zwłaszcza, że umieściliśmy textbox w gridzie). Zauważmy jednak, że MainWindow nie jest bezpośrednim rodzicem grida – znajduje się on dopiero na poziomie 4 w drzewie. Z racji dużego skomplikowania drzewa wizualnego aplikacji według mnie bezpiecznie jest używać funkcji Find z parametrem TreeScope.Descendants. Unikniemy dzięki temu przykrych niespodzianek z nieodnalezionymi kontrolkami. Nasza funkcja zatem powinna wyglądać w następujący sposób.

W analogiczny sposób znajdujemy pozostałe przyciski na naszej formie, a następnie postarajmy się wprowadzić do nich jakiś text. W celu interakcji z kontrolkami wykorzystamy tzw. Patterns
Patterns definiują określone funkcjonalności, które wspiera nasza kontrolka. Do najczęściej używanych patterns należą:

  • SelectionPattern – używany do manipulacjami kontrolkami wspierającymi zaznaczanie np.ListBox-ami
  • TextPattern – używany do manipulacjami kontrolkami wspierającymi edycję
  • ValuePattern – używany do pobierania i ustawiania wartości kontrolek nie wspierających wielokrotnych wartości
  • InvokePattern – używane do kontrolek wspierających wywołania – np. przyciski (wywołanie przyciśnięcia)
  • ScrollPattern – używane do kontrolek posiadających ScrolBar-y
  • RangeValuePattern – używany do kontrolek mogących posiadać jakiś zakres wartości np. ComboBox

Zatem w celu ustawienia wartości w textbox-ie posłużymy się następującym kodem

W analogiczny sposób ustawiamy zawartość drugiego textbox-a

Mając wprowadzone dane do textbox-ów wypadałoby teraz przeprowadzić obliczenia. Zatem musimy w jakiś sposób aby “nacisnąć” jeden z naszych czterech przycisków. Znajdźmy zatem nasze przyciski:

Następnie wykorzystując wspomniany wcześniej InvokePattern naciśnijmy przycisk “Dodaj”.

Ostatnim krokiem będzie zweryfikowanie czy wartość w textbox-ie txtResult jest zgodna z oczekiwaniami. W celu wyciągnięcia wartości z txtResult po raz kolejny posłużę się ValuePattern

Wynik otrzymany w textbox-ie porównujemy z oczekiwanym rezultatem

Na koniec najlepiej wrzucić nasz test w jakąś pętle, losowo generować wartości oraz porównywać je z wartościami oczekiwanymi. Dobrym pomysłem jest również stworzenie sobie jakiejś klasy pomocniczej, w której zostałyby umieszczone funkcje do wypełniania textbox-ow, klikania w buttony itp – gdyż jak widać dużo kodu jest powtarzalna. Ostatecznie aplikacja testowa może wyglądać w następujący sposób:

oraz klasa pomocnicza

result

UI Automation – czyli testy automatyczne w .NET