blog.testowka.pl

Automatyzacja

Trzech developerów – czyli krótka historia z morałem

opublikowany przez 09, Kwi, 2015, w kategoriach Agile, Automatyzacja, TDD, Testowanie, XP

5597863793_60f320a45d_o

Trzech programistów zostało zapytanych o to jak długo zajmie im przejście przez pole do domu?

Junior Developer popatrzył na dystans dzielący go od domu i powiedział: „Nie wygląda żeby było daleko  – zajmie mi to 10 minut”. 

Senior Developer popatrzył uważnie na pole i powiedział: „Powinienem być w stanie dotrzeć tam w ciągu dnia”. Oczywiście zdziwiło to Junior Developera. 

Ninja Developer popatrzył na pole i powiedział: „Wygląda na dziesięć minut drogi, ale myślę, że piętnaście minut będzie odpowiednią estymatą”

Junior Developer wystartował ale po kilku krokach wybuchła pierwsza mina zakopana na polu co sprawiło, że Junior Developer musiał zboczyć z najprostszej drogi. Po tym jak wielokrotnie musiał zawracać wysadzając po drodze kolejne miny, po dwóch dniach udało mu się dotrzeć do celu. Oczywiście nie obyło się bez ran.

Senior Developer od razu rozpoczął swoją podróż na czworaka uważnie sprawdzając każdy metr drogi i przemieszczając się tylko tam gdzie jest bezpiecznie. Oczwiście kilka razy natknął się na minę ale zdążył dotrzeć do celu w trochę ponad jeden dzień. 

Ninja Developer wystartował i spokojnie przeszedł przez pole do celu. Dotarł tam po dziesięciu minutach. 

Dwaj pozostali programiści zszokowani zapytali: „Jak udało Ci się to osiągnąć?”, „Jak przeszedłeś przez pole nie wysadzając żadnej miny?”

„To było łatwe” odpowiedział Ninja Developer – „Po prostu nie zakopałem tam tych min wcześniej”.

[znalezione na Quorze  i przetłumaczone na nasze]

Konkluzja: Emergent Architecture – fajna sprawa, ale warto czasem pamiętać o reużywalności i możliwościach rozwoju naszego kodu. Warto też mieć testy automatyczne, które przez takie pole minowe nas spokojnie, może trochę powoli, ale jednak bezpiecznie przeprowadzą.

 

 

Dodaj komentarz więcej...

Coding Dojo – TDD Kata

opublikowany przez 31, Mar, 2014, w kategoriach Agile, Automatyzacja, CI, TDD, XP

Kanji-shu-ha-ri507
Kilka tygodni temu miałem przyjemność poprowadzić dla jednego z moich klientów krótki warsztat w formie Coding Dojo. Na codzień z tymi zespołami pracujemy nad wdrożeniem Continuous Delivery oraz innych praktyk związanych z Agile. Dojo nie było częścią regularnego Agile Coachingu, ale pomyśleliśmy, że może być dobrą formą oderwania się od codziennej pracy, wyjścia ze strefy komfortu i nauczenia się czegoś nowego. I tak też chyba się stało.

Po pierwsze czym jest Coding Dojo?

Dojo to termin pochodzący z języka japońskiego oznaczający „miejsce treningu”. Odnosił się on do trenowania sztuk walki takich jak Kendo czy Aikido. Nie jest to zwykła sala treningowa, gdyż obowiązują na niej pewne zasady, wchodząc do Dojo zgadzamy się postępować zgodnie z obowiązującymi tam normami.

Coding Dojo to również miejsce gdzie chcemy potrenować nie tyle sztuki walki co sztuki tworzenia kodu. Tutaj też obowiązują pewne zasady, o których za chwilę…

Podczas Coding Dojo zgromadzeni programiści próbują rozwiązać określony problem. W naszym przypadku podeszliśmy do problemu „String Calculator” – prosty kalkulator obliczający sumę parametrów podanych na wejściu. Backlog do tego zadania udostępniłem tutaj.

Zasady

Dojo zorganizowaliśmy w formie TDD Kata oraz podzieliliśmy je na 4 iteracje. Każda iteracja to 30 minut kodowania, 6 minut na code review i 6 minut na wspólną retrospekcję.

Uczestnicy programowali w parach. Po każdej iteracji zmiana pary, poniżej więcej na ten temat.

W przeciwieństwie do typowego Code Retreat po każdej iteracji nie usuwaliśmy wytworzonego kodu tylko kontynuowaliśmy poprzednio zaczętą pracę.

Przebieg Coding Dojo

Pierwsza iteracja to dla większości uczestników pierwsze kroki w TDD (niektórzy mieli już pewną wprawę, niemniej jednak przypomnienie zasad każdemu się przydało) – programujemy zgodnie z zasadą Red -> Green -> Refactor. Co 3 minuty zmienia się osoba przy klawiaturze – to wymuszało komunikację. TDD Kata to ćwiczenie służące do doskonalenia swoich umiejętności.

Po blisko pół godziny tworzenia kodu przyszedł czas na Code Review, ze względu na to, że było około 20 osób postanowiliśmy, że w ramach Code Review wymieszamy pary, praca każdej pary musiała zostać przejrzana przez kogoś z innej pary. Ta praktyka pozwoliła bardzo szybko pokazać różnice pomiędzy parami i poszczególnymi uczestnikami, a następnie pozwoliła wymienić się zdobytą wiedzą i spostrzeżeniami.

I oczywiście wspólna retrospekcja, podczas której rozwiewaliśmy wszelkie wątpliwości i omawialiśmy problemy oraz spostrzeżenia.

W drugiej iteracji wprowadziłem dwie zmiany – po pierwsze postanowiliśmy stosować TDD ping-pong. Jest to metoda programowania w parze polegająca na tym, że pierwsza osoba pisze test, druga pisze funkcjonalność spełniającą ten test, pierwsza refaktoryzuje, druga pisze test itd. To ćwiczenie pozwoliło uczestnikom lepiej dostrzec różnice pomiędzy poszczególnymi etapami w pętli TDD, oraz to czym się różni TDD od Test-First.

Drugą zmianą (nie do końca w mojej ocenie udaną, powinienem zostawić to na trzecią iterację) był zakaz rozmawiania podczas kodowania. Znacznie lepiej było by, gdybyśmy wprowadzili to w następnej iteracji, po tym jak już uczestnicy opanowali podstawy pętli TDD. Celem wprowadzenia tej zasady było wymuszenie pisania na tyle czytelnego kodu by druga osoba mogła łatwo zrozumieć intencje. Niemniej jednak było to całkiem zabawne nawet pomimo tego, że jeszcze nie wszyscy zrozumieli dobrze zasady TDD w tej iteracji. Braki nadrobiliśmy w kolejnej.

Trzecia iteracja po Code Review i Retrospekcji była podobna do poprzedniej z tą różnicą, że teraz można było już rozmawiać. To zaowocowało ożywionymi dyskusjami, oraz dzieleniem się pomysłami, które nagromadziły się w poprzedniej iteracji, oraz podczas Code Review. W efekcie było dużo refactoringu.

Iteracja czwarta – „porzuć swój kod”. Ponieważ w życiu programisty bardzo często bywa tak, że musi grzebać się w kodzie, którego sam nie tworzył postanowiłem podczas naszego Coding Dojo również zrobić taki eksperyment. Tym razem zasada przy dobieraniu się w pary była prosta – wszyscy wstają od komputerów przy których siedzą i starają się dobrać w pary z kimś z kim jeszcze nie programowali oraz wspólnie usiąść przy komputerze przy którym jeszcze nie siedzieli.

Była to chyba jeden z najciekawszych eksperymentów jaki przeprowadzałem na ludziach (i ich kodzie) ;-). To niesamowite jak bardzo różnie można zaimplementować te same funkcjonalności oraz na jak wiele różnych pomysłów można wpaść w zależności od tego z kim się pracuje. Podczas tej iteracji niektórzy zawzięcie refaktoryzowali (pamiętając o zasadach TDD) inni starali się dodawać nowe funkcjonalności przyjmując zastane konwencje.

W ramach przedłużonego Code Review po tej iteracji poprosiłem by wszyscy wracali do wszystkich komputerów, przy których mieli okazję wcześniej pracować i dokładnie przejrzeli co stało się z „ich” kodem odkąd opuścili to miejsce (nie tylko w ostatniej iteracji ale także w poprzednich).

Wnioski

Pojawiło się kilka ciekawych pytań i wniosków oto kilka z nich:

„Tworząc kod w ten sposób widzę, że implementując w sumie prosty problem poprzez stosowanie TDD wydłużył się czas implementacji oraz tak na prawdę spadła jakość tego co zrobiłem – gdybym nie stosował TDD to zrobił bym to szybciej i lepiej”. 

Moja odpowiedź brzmiała: Tak! Gdybyś dzisiaj to zaimplementował bez TDD to zrobił byś to ładniej (i może nawet napisał byś testy). Niemniej jednak w TDD Kata chodzi właśnie o dążenie do perfekcji i doskonalenie swoich umiejętności. Tego typu ćwiczenia wykonujemy właśnie po to, by za którymś razem napisać ten kod jeszcze lepiej i szybciej używając TDD, gdyż ta metoda ma wiele innych zalet, które warte są poświęcenia czasu potrzebnego na jej dobre opanowanie.

„TDD jest proste, gdy tworzymy nową funkcjonalność – a co jeśli mam już sporo kodu i to nawet z testami i nagle przychodzi zmiana wymagań. Co jeśli mam na przykład 5000 testów? Mam teraz szukać i zmieniać wszystkie testy zanim wprowadzę zmiany?”

Moja odpowiedź: jeśli wiesz, które testy trzeba zmienić i możesz to w miarę prosto i szybko zrobić to to zrób. Jeśli nie wiesz to… napisz nowy test, wprowadź testowaną zmianę w zachowaniu aplikacji, odpal testy i zobacz które testy przestały przechodzić. Potem zastanów się czy testy, które przestały przechodzić zrobiły to. bo zmieniło się zachowanie, czy dlatego, że coś zepsułeś. Następnie przemyśl, czy poprawiając testy przypadkiem nie duplikujesz tych, które przed chwilą napisałeś. Jeśli nie, to zastanów się, czy zmiana w funkcjonalności, którą wprowadziłeś nie była przypadkiem zbyt duża i nie miała jeszcze innych niechcianych konsekwencji. Jeśli wszystko jest ok, to powtórz powyższą pętlę aż wprowadzisz wszystkie zmiany.

Moja ogólna rada – nie bójcie się czerwonych testów. One właśnie po to są by często nie przechodziły i pokazywały Wam status systemu. Jeśli zastosujecie zasady z Clean Code oraz dobrze przemyślicie swoje testy to analiza tego dlaczego nie przechodzą będzie łatwa i przyjemna, a co za tym idzie wniesie to znaczną wartość do Waszej pracy.

Reasumując

Coding Dojo w tej formie jest w mojej ocenie bardzo fajnym ćwiczeniem wartym polecenia każdemu zespołowi. Przedmiotem Dojo wcale nie musi być TDD Kata – możecie spróbować zmienić się z zupełnie innym problemem nawet niekoniecznie programistycznym. Najważniejsze jest to, by wykonywane ćwiczenia wnosiły jakąś wartość i pozwalały Wam poszerzyć wiedzę.

Dojo, które przeprowadziłem z zespołem B&T Skyrise miało na celu przede wszystkim poszerzenie wiedzy na temat TDD oraz nabranie wprawy w praktykach Code Review. Myślę, że cel został osiągnięty.

PS: Niezmiernie ciekawi mnie, czy któryś z uczestników spróbował później podejść do samodzielnego rozwiązania jakiejś TDD Katy… Sprawdzę to przy najbliższej okazji.

1 komentarz więcej...

David Evans w Warszawie

opublikowany przez 10, Sie, 2013, w kategoriach Agile, Automatyzacja, Testowanie

23 Września 2013 roku w Warszawie odbędą się pierwsze Mistrzostwa Polski w Testowaniu Oprogramowania. Wydarzenie zapowiada się ciekawie.

Również i ja będę miał przyjemność opowiedzieć Wam podczas Mistrzostw o tym w jaki sposób efektywnie automatyzować testy i stosować BDD!

Natomiast jeszcze ciekawiej zapowiada się szkolenie „Testowanie w Agile” prowadzone przez Davida Evansa – jednego z głównych propagatorów technik takich jak Specification by example.

Osobiście liczę na to, że będę miał okazję wymienić swoje doświadczenia związane z tworzeniem wymagań i kryteriów akceptacji z autorytetem w tej dziedzinie. Kto wie – może dowiem się czegoś nowego, a już na pewno będę chciał zweryfikować swoje pomysły oraz to, jak do tej pory wykorzystywałem narzędzia takie jak Impact Mapping czy Story Mapping.

Was też zachęcam do wzięcia udziału w spotkaniu z jednym z najbardziej doświadczonych praktyków testowania w Agile.

Więcej o samym szkoleniu możecie znaleźć tutaj.

1 komentarz więcej...

Kurs Selenium część 5 – Ładne testy

opublikowany przez 17, Sty, 2013, w kategoriach Automatyzacja, Kurs Selenium, Testowanie

Całość kursu dostępna tutaj

Stwórzmy troszeczkę bardziej skomplikowany test. Spróbujmy się zalogować do http://demo.bananascrum.com

@Test
public void test1() {
	driver.get("http://demo.bananascrum.com/login");
	WebElement a = driver.findElement(By.id("login")); //pole z loginem
	a.sendKeys("admin"); //wpisuje login
	WebElement b = driver.findElement(By.id("password"));  //pole z haslem
	b.sendKeys("password");//wpisuje haslo
	WebElement c = driver.findElement(By.name("commit")); //klikam zatwierdz
    c.click();
	driver.findElement(By.id("admin")).isDisplayed(); //Sprawdź czy zalogowany
}

Czy coś z tym testem jest nie tak?
Przecież działa…

Przede wszystkim problemem są w nim komentarze. Jeśli nasz kod potrzebuje komentarzy to znaczy, że nie jest wystarczająco przejrzysty. I tak własnie jest w przypadku tego kodu.

Zanim zabiorę się do komentarzy wypadało by się zastanowić co chcemy przetestować – z tego testu nie wynika to wprost.

Przede wszystkim należało by zmienić nazwę.

@Test
public void shouldBePossibleToLogInWithCorrectData() {
...

Używam konwencji z „should” na początku – dzięki temu nazwa testu zawsze wskazuje na to co chcemy przetestować.
Nazwa ma dodatkowo bardzo istotne znaczenie – raczej staramy się by nasze testy były jak najbardziej atomowe i testowały zawsze jedną rzecz. Dzięki takiemu podejściu informacja jaką nam dają jest zawsze przejrzysta – dokładnie wiemy co się zepsuło i gdzie szukać błędów.

Jeśli w nazwie swojego testu wpisujesz „and” albo „or” to wiedz, że coś się dzieje…

Komentarze…

Z komentarzami główny problem jest taki, że przeważnie są nieaktualne i mylące – o ile w momencie gdy piszemy kod bywają pomocne to z czasem tylko przeszkadzają. Pamiętajmy o tym, że nasz kod żyje swoim życiem i cały czas się rozwija.

Jak się ich pozbyć? Można na przykład stosować odpowiednie nazwy metod.

Krok pierwszy – Extract Method.

@Test
public void shouldBePossibleToLogInWithCorrectCredentials() {
	openLoginPage();
	logIn();
	checkIfLogedIn();
}

private void checkIfLogedIn() {
	driver.findElement(By.id("admin")).isDisplayed();
}

private void logIn() {
	WebElement a = driver.findElement(By.id("login")); //pole z loginem
	a.sendKeys("admin"); //wpisuje login
	WebElement b = driver.findElement(By.id("password"));  //pole z haslem
	b.sendKeys("password");//wpisuje haslo
	WebElement c = driver.findElement(By.name("commit")); //klikam zatwierdz
    c.click();
}

Polecam używać skrótu klawiszowego ALT+SHIFT+M. Zaznaczamy kawałek kodu i wciskamy skrót – pozostaje podanie nazwy metody.

Wytworzone w ten sposób metody pozostawiają wiele do życzenia.

Zwłaszcza metoda logIn() nadal wymaga komentarzy…

private void logIn() {
	WebElement login = driver.findElement(By.id("login"));
	WebElement password = driver.findElement(By.id("password"));
	WebElement commit = driver.findElement(By.name("commit")); 

	login.sendKeys("admin");
	password.sendKeys("password");
	commit.click();
}

Teraz wygląda to trochę lepiej. Nie jest jeszcze perfekcyjnie ale do tego przejdziemy później.

Krok 2 – struktura testów.

@Test
public void shouldBePossibleToLogInWithCorrectCredentials() {
	openLoginPage();
	logIn();
	checkIfLogedIn();
}

Powyższy test wygląda już całkiem nieźle.

Widać w nim pewną strukturę:
– najpierw przygotowujemy sobie środowisko testowe (otwieramy testowaną stronę)
– następnie wykonujemy testowaną akcję
– i weryfikujemy czy akacja przyniosła oczekiwany efekt.

Powyższą strukturę można opisać jako Given, When, Then (konwencja znana praktykom BDD). Given – kontekst testu, When – akcja do wykonania (zawsze jedna), Then – sprawdzenie poprawności wykonywanej akcji, asercje.

@Test
public void shouldBePossibleToLogInWithCorrectCredentials() {
	// GIVEN
	openLoginPage();
	// WHEN
	logIn();
	// THEN
	checkIfLogedIn();
}

To by było na tyle jak na początek. Więcej w praniu…
Polecam książkę Clean Code – Robert C. Martin.
Czystość i przejrzystość testów to podstawa jeśli zależy nam na ich utrzymywalności i rozwijalności w przyszłości. A zależy nam na tym zawsze – inaczej nie było by sensu pisać testów.

W następnych częściach kursu będzie o tym jak parametryzować testy oraz rozpoczniemy przygodę z wzorcami projektowymi. Nasze testy nadal nie są obiektowe – a warto by było wykorzystać w pełni możliwości języków obiektowych takich jak Java.

Dodaj komentarz więcej...

Kurs Selenium część 4 – Pierwszy Test

opublikowany przez 22, Lis, 2012, w kategoriach Automatyzacja, Kurs Selenium, Testowanie

Całość kursu dostępna tutaj
Potrzebujemy zaimportować odpowiednie zależności:

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.testng.annotations.Test;

Powyższe powinno wystarczyć
Aby nasze testy mogły korzystać z selenium driver potrzebujemy zainicjować obiekt WebDriver oraz potrzebujemy adnotacji @Test.

@Test
public AppTest(String testName) {
	WebDriver driver = new FirefoxDriver();
	driver.get("http://blog.testowka.pl");
        driver.close();
}

Teraz wystarczy trochę posprzątać. Przede wszystkim wyciągamy inicjację drivera do metody setUp() oznaczoną adnotacją @BeforeClass.

@BeforeClass
public void setUp() {
	driver = new FirefoxDriver();
}

Następnie zamykanie przeglądarki do metody tearDown() oznaczonej adnotacją @AfterClass

@AfterClass
public static void tearDown() {
	driver.close();
}

Nasz test powinien wyglądać tak:

import org.testng.annotations.AfterClass;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;

public class AppTest {

	static WebDriver driver;

	@BeforeClass
	public void setUp() {
		driver = new FirefoxDriver();
	}

	@Test
	public void ShouldTestowkaPlPageBeOpenable() {
		driver.get("http://blog.testowka.pl");
	}

	@AfterClass
	public static void tearDown() {
		driver.close();
	}
}

Jak zapewne zauważyliście nasz test jeszcze niczego nie testuje, gdyż nie ma w nim asercji. Dodajemy więc asercje sprawdzającą czy tytuł strony jest poprawny.

Importujemy bibliotekę z asercjami:

import static org.testng.Assert.*;

Dodajemy asercje do testu:

@Test
public void ShouldTestowkaPlPageBeOpenable() {
	driver.get("http://blog.testowka.pl");
	assertEquals(driver.getTitle(), "blog.testowka.pl");
}

 

6 komentarzy więcej...

Kurs Selenium część 3 – Operacje na elementach stron WWW

opublikowany przez 19, Lis, 2012, w kategoriach Automatyzacja, Kurs Selenium, Testowanie

Całość kursu dostępna tutaj

Selenium WebDriver jest biblioteką w pełni obiektową. Zgodnie z założeniem wszystko w tym także elementy na stronie są obiektami. Dzięki takiemu podejściu możemy elementy wyszukiwać i zapisywać w pamięci jako obiekty, po czym wykonywać akcje na obiektach.

Model obiektowy przydaje się także podczas modelowania struktury naszych testów, ale o tym będzie w kolejnych rozdziałach.

Najpierw inicjalizujemy obiekt typu WebElement

import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;

 

WebElement linkToAgilePage = driver.findElement(By.linkText("Agile"));

Istnieje wiele różnych sposobów na wyszukiwanie elementów na stronie.
Aby sprawdzić czego możemy użyć wpisujemy w Eclipse:

driver.findElement(By.

i wciskamy [SPACJA]. IDE podpowie nam czego możemy użyć.

Najczęściej używane (przeze mnie) to:

By.id();
By.name();
By.linkText();
By.xpath();

Dzieki takiej różnorodności możemy wybrać metodę poszukiwania elementów na stronie, która będzie najmniej ryzykowna. Chodzi tutaj o ryzyko tego, że dany element wkrótce może się zmienić albo nie będzie unikatowy.

Następnie możemy wykonywać już akcje na wyszukanych obiektach.

linkToAgilePage.click();

Przykładowy test z kilkoma akcjami wykonanymi na elementach strony znajdziecie poniżej. W następnym rozdziale zajmiemy się czyszczeniem naszych testów tak by były bardziej fczytelne.

package pl.testowka;

import static org.testng.Assert.*;
import org.testng.annotations.AfterClass;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;

public class TestowkaTest {

	static WebDriver driver;

	@BeforeClass
	public void setUp() {
		driver = new FirefoxDriver();
	}

	@Test
	public void ShouldBePosibleToSearchTestowkaAtGoogle()
			throws InterruptedException {
		driver.get("http://google.pl");
		WebElement searchField = driver.findElement(By.name("q"));
		searchField.sendKeys("testowka.pl");
		WebElement searchButton = driver.findElement(By
				.xpath("//*[@id=\"gbqfb\"]/span"));
		searchButton.click();
		for (int second = 0;; second++) {
			if (second >= 60)
				fail("timeout");
			try {
				if (driver.findElement(By.linkText("Testowka.pl"))
						.isDisplayed()) {
					break;
				}
			} catch (Exception e) {
			}
			Thread.sleep(1000);
		}
		WebElement linkToTestowka = driver.findElement(By
				.linkText("Testowka.pl"));
		linkToTestowka.click();
	}

	@AfterClass
	public static void tearDown() {
		driver.close();
	}

}

 

5 komentarzy więcej...

Kurs Selenium część 2 – Maven i zależności

opublikowany przez 13, Lis, 2012, w kategoriach Automatyzacja, Kurs Selenium, Testowanie

Całość kursu dostępna tutaj

Aby utworzyć nowy projekt przy użyciu Maven wchodzimy do katalogu workspace wskazanego w eclipse i w konsoli wpisujemy:

mvn archetype:generate

Kolejno odpowiadamy na pytania:
Pierwsze zostawiam default: ENTER
Wersję maven:  zostawiam domyślnie  ENTER
GroupID : wpisuję nazwę np. „testowka”
ArchetypeID: wpisuję np. „pl”
Wersję projektu:  zostawiam domyślnie ENTER
Package: zostawiam domyślnie jako „testowka”

Następnie sprawdzamy czy projekt się kompiluje.

mvn clean install

Otwieramy Eclipse klikamy prawym przyciskiem myszy na drzewie projektów i wybieramy Import -> Maven Project -> wybieramy katalog utworzonego projektu.

Dla pewności w Eclipse odpalamy Run As -> Maven Install.

W eclipse klikamy prawym na plik pom.xml i wybieramy Maven -> Add dependency. Wpisujemy w wyszukiwarkę TestNG wybieramy org.testng testng, następnie to samo dla Firefox org.seleniumhq.selenium selenium-firefox-driver.

Teraz już możemy używać selenium WebDriver w naszych testach odpalanych przy pomocy TestNG.

Nasz plik pom.xml powinien wyglądać mniej więcej tak:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>testowka</groupId>
  <artifactId>pl</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>test</name>
  <url>http://maven.apache.org</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencies>

    <dependency>
    	<groupId>org.testng</groupId>
    	<artifactId>testng</artifactId>
    	<version>6.8</version>
    </dependency>
    <dependency>
    	<groupId>org.seleniumhq.selenium</groupId>
    	<artifactId>selenium-chrome-driver</artifactId>
    	<version>2.42.2</version>
    </dependency>
    <dependency>
    	<groupId>org.seleniumhq.selenium</groupId>
    	<artifactId>selenium-firefox-driver</artifactId>
    	<version>2.42.2</version>
    </dependency>
  </dependencies>
</project>
12 komentarzy więcej...