SpecFlow + Mailosaur = Łatwiejsze testowanie maili

Testowanie maili z SpecFlow i Mailosaur

Cześć, dawno mnie tu nie było. Pisałem ostatnio kilka wpisów dla testuj.pl również będą się tam pojawiać od czasu do czasu moje wpisy, więc zachęcam was do sprawdzania serii o SpecFlow (link).

Dzisiaj zajmiemy się tematem – testowaniem treści mail. Tworząc test automatyzujący akcje, które musielibyśmy zrobić.

Jak podejść do testowania automatycznego maili, które przychodzą na skrzynkę mailową?

W czystym Selenium jest to możliwe jednak nie, jest to proste jak z narzędziem, które pokażę wam dzisiaj.  Z pomocą przychodzi nam Maliosaur. Nie użyjemy dziś Selenium więc nasz test będzie testował funkcjonalne, ale poprzez dostęp do API Mailosaura, które ma napisanego klienta dla .NET-u (Stworzony został wrapper API dla .NET-u)

Testowanie automatyczne zapomnianego hasła lub innych mail z systemu? 

Naprawdę może być prostsze.

Czego potrzebujemy?

  • Visual Studio 2017;
  • Stworzenie konta na mailosaur – W wersji trial możemy korzystać z konta przez 14 dni;
  • SpecFlow – nie jest niezbędny, ale nasz przykład będzie korzystał z niego;
  • R# – nie jest niezbędny, ale używam go do uruchamiania testów, oczywiście może również być NUnit Console Runner;
  • Testowa strona – w moim przypadku jest to WordPress, ale może być dowolna inna strona, która interesuje was.
  • Kod dodałem na GitHubie – we wpisie każdy z kroków i poszczególne klasy są omówione. https://github.com/testingplusme/MailosaurSpecFlow

Zaczniemy od utworzenia projektu typu class library w Visual Studio 2017. Tworzymy strukturę katalogów:

specflow automatyzacja testowanie maila - struktura katalogów

Struktura katalogów naszego projektu

 Mailosaur korzyści:

  • Przyjazne API, które pozwala testować nam poprawność treści mail (HTML), linki do obrazków wraz z podpisem, zgodność nadawcy, adresata lub adresatów
  • Szybciej napiszemy test dla formularzy takich jak przypominanie hasła niż standardowo w Selenium WebDriver.
  • Czas -taki test może trwać kilka – kilkadziesiąt sekund zależnie od złożoności. Test UI zająłby więcej czasu.

Po dodaniu folderów dodajemy do NuGet Packages

  • Mailosaur 3.0.2
  • HtmlAglityPack 1.7.0
  • SpecFlow 2.3.0
  • NUnit 3.9.0

Nasz scenariusz będzie sprawdzał, czy mamy w naszej skrzynce maila tytułem „Welcome to WordPress.com”. W kolejnym kroku sprawdzał, będzie czy nadawca, oraz odbiorca zgadzają się z tymi, które zakładaliśmy. W tej metodzie również dokonujemy asercji priorytetu maila oraz czy dobry jest host nadawcy.

W następnym kroku sprawdzimy, czy obrazek, który powinien być w tym mailu jest, wraz z adresem i podpisem do niego. W ostatnim kroku dokonamy sprawdzenia poprawności html maila, który do nas przyszedł zgadza się z tym co byśmy sami zaimplementowali. W tym ostatnim kroku porównamy te dwa html. Jeden to będzie html wzięty z maila, drugi nasz, który napisalibyśmy lub mieli jego implementacje (W naszym przypadku WordPress zrobił to).

Listing  – Scenariusz EmailsInWordpressFeature

specflow mailosaur - feature file

SpecFlow Feature – Scenariusz, który zaimplementujemy

Zaczynamy.

Przechodzimy do dodania naszego scenariusza do folderu Features. Plik nazywam jako EmailsInWordpressFeature.feature

Następnie dodajemy klasę EmailSteps w niej będą znajdywać się wszystkie potrzebne kroki.

1) Krok –  „I check view of  „<Subject>” mail

Listing – EmailSteps – zawierająca wszystkie potrzebne kroki w naszym scenariuszu

EmailSteps.cs

EmailSteps.cs – wszystkie kroki

EmailsSteps – konstruktor

Rozpoczynamy od dodania dziedziczenia po klasie BaseStep. Jest to klasa pomocnicza, która zawiera pola protected, które używamy w klasie EmailSteps.

BaseStep - klasa bazowa

BaseStep – klasa bazowa z, której dziedziczy nasza klasa kroków

Wracamy do naszego pliku EmailSteps. Zaczynamy od zdefiniowania potrzebnych parametrów w konstruktorze i inicjalizacji ich.

Kolejnym krokiem jest utworzenie metody GivenICheckViewOfMail dla kroku  -> „I check view of „”(.*)”” mail”.

W linijce [23] korzystamy z klasy MailosaurHelper

MailosaurHelper

MailosaurHelper

zawierająca metodę TakeEmails(), która zwraca maile dla naszego maila. Nasz mail mamy zdefiniowany w klasie TestSettings (implementacja niej). Dodatkowo dodajemy metodę Single(), która zwróci nam mail z określonym tematem. Używamy metody Single() dlatego, że spodziewamy się tylko i wyłącznie jednego maila z tym tematem, więc jeżeli byłoby więcej maili o tym tytule to aplikacja w tym miejscu zwróci błąd.

W linijkach [24-25] klasy EmailSteps korzystamy z EmailContext’u.

I check correctness of html code

I check correctness of html code

EmailContext

EmailContext – przechowywanie wartości pomiędzy krokami


Jest to klasa, która zawiera wszystkie potrzebne właściwości z, których będziemy korzystać w całym projekcie. ScenarioContext pozwala przechowywać różnego typu wartości podczas testu i mieć do nich dostęp pomiędzy krokami. Kod jest znacznie przyjemniejszy do utrzymywania, gdy korzystamy ze wstrzykiwania zależności w kodzie. Dodatkowo uruchomienie naszych testów w sposób współbieżny byłoby dość łatwe, bo korzystamy w tym miejscu z Context Injection.

Na marginesie: Jeżeli korzystalibyśmy ze statycznej klasy ScenarioContext.Current to kod nie będzie działaj współbieżnie. Spowodowane jest to tym, że SpecFlow ma problem (i słusznie) z rozpoznaniem o, który Current nam chodzi.  Lepiej korzystać z Context Injection.

W linijce [26] klasy EmailSteps kończymy metodę asercją, która sprawdza, czy temat maila zgadza się z tym, który przekazaliśmy w kroku.

We właściwości MailBox przekazujemy nasz mailBoxId oraz secretKey.

2) Krok –  „I check:”

SpecFlow feature - Metoda "I check"

SpecFlow feature – Metoda „I check”

Metoda "I check"

EmailRow

EmailRow

 

Metoda GivenICheck korzysta z możliwości mapowania rekordu tabeli na obiekt w SpecFlow (Opisywałem to szerzej dla testuj.pl – link). Metoda CheckBasicInfo jako parametr przyjmuje listę wierszy, które mieliśmy w tym kroku. EmailRow jest klasą pomocniczą, aby zmapować poszczególne wiersze tabeli.

W klasie MailosaurHelper dodajemy metodę CheckBasicInfo, która będzie sprawdzać te podstawowe dane przekazane z tabeli. Krok „I check: ” korzysta z niej w linijce [33] pliku

Metoda CheckBasicInfo

Metoda CheckBasicInfo

Metoda dokonuje asercji, czy w liście, która zawiera wartości z wierszy tabeli, tą, którą przekazaliśmy zgadz nam się adresat, nadawca, oraz priorytet wysyłanej wiadomości i senderHost. Używamy w linijce [28] asercji emailContext.CurrentEmail.To[0].Address, ponieważ spodziewamy się jednego adresata. Jeżeli chcielibyśmy sprawdzić sytuacje z większą ilością adresatów to w tym miejscu trzeba byłoby np. wylistować wszystkich i dodać metodę, która dokona asercji warunku jakiego chcieliśmy.

Krok 3 ) „I check correctness of images on mail: „

Listing – CheckImages metoda, która sprawdza poprawność źródła (Src) oraz podpisu (alt) dla obrazu /obrazów

Krok w EmailsInWordpressFeature

Krok w EmailsInWordpressFeature

I check correctness of images on mail:

Kroki „I check correctness of images on mail: ” w EmailSteps

EmailImage jest to klasa pomocnicza dla parsowania wiersza tabeli na obiekt. W pierwszej kolumnie podajemy adres do obrazka w drugiej podpis, jeżeli jest

EmailImage

EmailImage – klasa używana dla mapowania tabeli – sprawdzanie poprawności adresu oraz podpisu do obrazków.

Metoda CheckImages(emailIamges) która sprawdza, czy url (lub url, bo parametrem jest List<EmailImages> emailImages)

Metoda CheckImage

Metoda CheckImage

i podpis do obrazka, który podaliśmy w tabeli znajduje się w mailu, który chcemy sprawdzić. Używamy SingleOrDefault(), bo spodziewamy się tylko i wyłącznie jednego elementu, ale przyjmujemy również, że wyniki tej kwerendy może zwrócić nulla. Dlaczego tak? Dlatego, że w asercji sprawdzamy, czy ten obiekt jest nullem. Możemy dodać zdefiniowany komunikat do tej asercji i może powiedzieć nam to więcej. Np. „We ‚re expecting contain image” może nam to pomóc w debugowaniu. Moglibyśmy również użyć metody Single(), wtedy, jeżeli tego elementu by nie było to test podczas uruchomienia zwróciłby wyjątek „Sequence contains no elements”. W tym miejscu użyłem tego drugiego sposobu. Chce wam pokazać, że mamy przynajmniej dwie możliwości podejścia do tego typu sytuacji. Zachęcam żebyście wybrali tą, która wam bardziej pasuje. Assert.IsNotNull z dodatkowym komunikatem może przekazywać bardziej trafioną informacje niż „Sequence contains no elements”.

Krok  4 ) „I check correctness of html code”

I check correctness of html code

I check correctness of html code

Listing – CompareHtml metoda porównująca dwa html

Klasa TestSettings zawiera mail, którego używamy w teście oraz ścieżkę do pliku html dla formularza powitalnego WordPressa

TestSettings - klasa zawierająca podstawowe ustawienia

TestSettings – klasa zawierająca podstawowe ustawienia

CompareHtml

Metoda CompareHtml

CaptureHtml jest to metoda, która przyjmuje dwa parametry. Pierwszy to string, który ma być w domyślnie przekazanym html a drugi parametr również string, ale używany do wskazania gdzie nasz plik tekstowy z html się znajduję. Html Agility Pack (Dodaliśmy przez NuGeta), którego używamy posiada m.in. te dwie możliwości wczytania html z pliku oraz ze zmiennej typu string.

Wczytujemy do dwóch zmiennych html i w linijce [41] porównujemy asercją czy zgadzają się oba html.  Używamy tutaj dodanej właściwości w klasie TestSettings – PathHtmlWelcomeTxt.

Przechodzimy do uruchomienia naszego testu. R# – > Session Tests – > Run.

specflow automatyzacja testowanie maila - wynik testu

Pozytywny rezultat testu

Naszym oczom ukazuje się czas wykonania testu niecałe trzy sekundy.

Podsumowanie

Dzisiaj dowiedzieliśmy się w jak (dość) prosty sposób testować treści wysyłanych przez nas mail. Wiem, że są serwisy, które posiadają kilkadziesiąt typów różnego rodzaju maili, więc takie rozwiązanie może być znacznym ułatwieniem pracy w zespole oraz unikanie regresji. Jeżeli korzystacie np. z MailGuna to on, również posiada przyjemne API, które proste jest do zautomatyzowania. Zachęcam do udostępniania treści i podzielenia się jak wy testujecie wasze maile.

 

michalslezak

 

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *