4. Связка с TestNG.
Теперь попробуем увязать нашу логику и TestNG.
Первая мысль - «Надо просто сделать метод и пометить его аннотацией @Test». Так:
@Test
public
void
testGame(){
WebdriverHelper.openUrl("http://testsite/gamepage/");
GenieScriptsExecutor.ExecuteScript("path
to Genie script .class");
WebdriverHelper.waitforElementDisplayed(By.id("gameEnd"));
}
Но здесь есть проблема. Genie написан так, что прогон скрипта осуществляется в отдельном потоке. Все эксепшны, которые образуются при прогоне скрипта перехватываются и обрабатываются самим Genie в том же потоке. Таким образом, даже если при выполнении команды GenieScriptsExecutor.ExecuteScript("path_to_geniescript.class");
возникнут какие-то ошибки, то они будут обработаны внутри потока и TestNG будет считать, что всё в порядке.
Что делать?
- Можно переписать сам Genie, добавить проброску RuntimeException-ов, ловить их и пробрасывать выше. Но перекидывание исключениями между потоками не считается хорошим тоном.
- Можно пойти другим путём. Результаты прогона скрипта Genie держит в xml файлике. Мы можем парсить этот файлик на предмет наличия там сообщений об ошибках. Велосипед тот ещё, но я не вижу других вариантов решения проблемы.
Итак, парсим. Сначала разберёмся, как нам получить xml с результатами прогона скрипта. Если посмотреть в класс Executor на метод startExecution, то можно видеть, что он возвращает некий GenieExecutionResult.
public
static
GenieExecutionResult startExecution(String[] args)
Заглянем в класс GenieExecutionResult. Внутри его есть две приватные переменные
private
String execResultXML;
private
boolean
finalResult;
А также геттер getTestResultXML(); Именно это нам и нужно. Есть разные варианты парсинга xml файлов. Можно просто грубо ковыряться в DOM-е. Можно, например, попробовать преобразовать xml в объект. Программисты называют это десериализацией и мы её сейчас сделаем.
Разберём работу по пунктам:
- Создаём модельный класс
- Преобразуем (десериализуем) xml в объект
- Просматриваем объект
4.1 Создаём модельный класс
Я вынесу все, что касается парсинга в отдельный пакет. Назовём его com.blogspot.testerstorehouse.parser. Модельный класс это по сути — объектное представление xml файла.
package
com.blogspot.testerstorehouse.parser;
import
java.util.ArrayList;
import
javax.xml.bind.annotation.XmlAttribute;
import
javax.xml.bind.annotation.XmlElement;
import
javax.xml.bind.annotation.XmlRootElement;
import
com.blogspot.testerstorehouse.parser.TestResultsModel.TestCase.TestResults.TestParameter;
@XmlRootElement(name
= "TestLog")
public
class
TestResultsModel {
@XmlElement(name
= "TestSettings")
public
TestSettings testSettings;
@XmlElement(name
= "TestCase")
public
ArrayList<TestCase> testCases;
public
static
class
TestSettings{
@XmlElement(name
= "TestEnvironment")
TestEnvironment
testEnvironment;
public
static
class
TestEnvironment{
@XmlElement(name
= "TestEnvironment")
TestMachine
testMachine;
@XmlElement(name
= "GenieVersionInfo")
GenieVersionInfo
genieVersionInfo;
public
static
class
TestMachine{
@XmlAttribute(name
= "availableMemory")
String
availableMemory;
@XmlAttribute(name
= "osArchitecture")
String
osArchitecture;
@XmlAttribute(name
= "physicalMemory")
String
physicalMemory;
@XmlAttribute(name
= "processsorCount")
String
processorCount;
@XmlElement(name
= "TestSetup")
TestSetup
testSetup;
@XmlElement(name
= "JavaSetup")
JavaSetup
javaSetup;
public
static
class
TestSetup{
@XmlAttribute(name
= "locale")
String
locale;
@XmlAttribute(name
= "os")
String
os;
@XmlAttribute(name
= "osVersion")
String
osVersion;
@XmlAttribute(name
= "platform")
String
platform;
@XmlAttribute(name
= "userTimezone")
String
userTimezone;
}
public
static
class
JavaSetup{
@XmlAttribute(name
= "javaRuntimeVersion")
String
javaRuntimeVersion;
@XmlAttribute(name
= "javaVersion")
String
javaVersion;
}
}
public
static
class
GenieVersionInfo{
@XmlElement(name
= "ServerVersion")
String
ServerVersion;
@XmlElement(name
= "ExecutorVersion")
String
ExecutorVersion;
@XmlElement(name
= "PluginVersion")
String
PluginVersion;
}
}
}
public
static
class
TestCase{
@XmlElement(name
= "TestScript")
TestScript
testScript;
public
static
class
TestScript{
@XmlAttribute(name
= "name")
String
name;
@XmlElement(name
= "Messages")
String
messages;
@XmlElement(name
= "TestResults")
TestResults
testresults;
@XmlElement(name
= "TestStep")
public
ArrayList<TestStep>testSteps;
}
public
static
class
TestResults{
@XmlAttribute(name
= "status")
String
status;
@XmlElement(name
= "TestTime")
TestTime
testTime;
@XmlElement(name
= "TestParameter")
TestParameter
testParameter;
public
static
class
TestTime{
@XmlAttribute(name
= "duration")
String
duration;
@XmlAttribute(name
= "end")
String
end;
@XmlAttribute(name
= "start")
String
start;
}
public
static
class
TestParameter{
@XmlAttribute(name
= "name")
String
name;
@XmlAttribute(name
= "type")
String
type;
@XmlAttribute(name
= "value")
String
value;
}
public
static
class
TestStep{
}
}
public
static
class
TestStep{
@XmlElement(name
= "TestResults")
TestResults
testresults;
@XmlElement(name
= "TestParameter")
TestParameter
testParameter;
@XmlElement(name
= "Message")
Message
message;
public
static
class
Message{
@XmlAttribute(name
= "message")
String
messagetext;
@XmlAttribute(name
= "type")
String
type;
}
}
}
}
Создадим также класс, в котором будут лежать нужные нам результаты.
package
com.blogspot.testerstorehouse.parser;
public
class
ShortTestResult {
private
String message
= "";
private
String executionStatus
= "Passed";
public
String getExecutionStatus() {
return
executionStatus;
}
public
void
setExecutionStatus(String status) {
this.executionStatus
= status;
}
public
String getMessage() {
return
message;
}
public
void
setMessage(String resultmessage) {
this.message
= resultmessage;
}
}
По сути, нам нужны только сообщения об ошибках и общий результат выполнения скрипта.
4.2 Преобразуем (десериализуем) xml документ в объект
Если вы помните, то результат работы скрипта возвращается в виде строки. Это ещё не документ. Создадим ещё один класс — Parser. В нём сложим методы, непосредственно преобразующие строку в документ и документ в объект. Напишем метод getXMLFromString который преобразует строку в документ. После этого преобразуем документ в объект. Воспользуемся библиотечкой JAXB для этой цели. Там процес десериализации называется демаршализацией. Пусть так, главное, что работает. Смотрим метод DeserializeXML
4.3 Просматриваем объект
Просматривать объект мы будем банально в цикле. Смотрите метод getShortExecutionResult ниже.
package
com.blogspot.testerstorehouse.parser;
import
java.io.StringReader;
import
java.util.ArrayList;
import
javax.xml.bind.JAXBContext;
import
javax.xml.bind.JAXBException;
import
javax.xml.parsers.DocumentBuilder;
import
javax.xml.parsers.DocumentBuilderFactory;
import
org.w3c.dom.Document;
import
org.xml.sax.InputSource;
import
com.blogspot.testerstorehouse.parser.TestResultsModel.TestCase;
import
com.blogspot.testerstorehouse.parser.TestResultsModel.TestCase.TestStep;
public
class
Parser {
Document
filetoparse
= null;
//Просматриваем
объект и делаем вытяжку в короткий
результат ShortTestResult
public
static
ShortTestResult getShortExecutionResult(String xml){
ShortTestResult
str = new
ShortTestResult();
TestResultsModel
parsedresult = DeserializeXML(xml);
ArrayList<TestCase>
testcases = parsedresult.testCases;
String
errorMessages = "";
for
(TestCase testCase : testcases) {
ArrayList<TestStep>
testSteps = testCase.testScript.testSteps;
for
(TestStep testStep : testSteps) {
String
teststatus = testStep.testresults.status;
if(teststatus.equals("Failed")){
str.setExecutionStatus(teststatus);//если
хоть один шаг упал, то мы будем считать
весь тест упавшим
}
if
(testStep.message!=null){
//Собираем
все сообщения об ошибках
errorMessages
+= testStep.message.messagetext
+"\n";
}
}
}
str.setMessage(errorMessages);
return
str;
}
//Превращаем
документ в объект
private
static
TestResultsModel DeserializeXML(String xml){
TestResultsModel
testResultsModel=null;
InputSource
is = new
InputSource(new
StringReader(xml));
JAXBContext
jaxbContext;
try
{
jaxbContext
= JAXBContext.newInstance(TestResultsModel.class);
javax.xml.bind.Unmarshaller
jaxbUnmarshaller = jaxbContext.createUnmarshaller();
testResultsModel
= (TestResultsModel) jaxbUnmarshaller.unmarshal(is);
}
catch
(JAXBException e) {
e.printStackTrace();
}
return
testResultsModel;
}
//Преобразуем
строку в документ
public
static
Document getXMLFromString(String xml) throws
Exception{
DocumentBuilderFactory
factory = DocumentBuilderFactory.newInstance();
DocumentBuilder
builder = factory.newDocumentBuilder();
InputSource
is = new
InputSource(new
StringReader(xml));
Document
document = builder.parse(is);
document.normalize();
return
document;
}
}
Теперь добавим парсинг результатов в наш метод ExecuteScript.
package
com.blogspot.testerstorehouse.geniehelper;
import
com.adobe.genie.executor.Executor;
import
com.adobe.genie.executor.objects.GenieExecutionResult;
import
com.blogspot.testerstorehouse.parser.Parser;
import
com.blogspot.testerstorehouse.parser.ShortTestResult;
public
class
GenieScriptsExecutor {
public
static
ShortTestResult ExecuteScript(String path) {
String[]
path_arr = new
String[]{path};
ShortTestResult
result = new
ShortTestResult();
try
{
GenieExecutionResult
scriptExecResult = Executor.startExecution(path_arr);
return
Parser.getShortExecutionResult(scriptExecResult.getTestResultXML());
}
catch
(ClassNotFoundException e) {
e.printStackTrace();
}
return
result;
}
}
5. Тесты
Теперь у нас всё готово и осталось только написать тесты.
package
com.blogspot.testerstorehouse.tests;
import
org.openqa.selenium.By;
import
org.testng.Assert;
import
org.testng.annotations.Test;
import
com.blogspot.testerstorehouse.geniehelper.GenieScriptsExecutor;
import
com.blogspot.testerstorehouse.parser.ShortTestResult;
import
com.blogspot.testerstorehouse.webdriverhelper.WebdriverHelper;
public
class
GameTests extends
TestsBase{
@Test
public
void
testGame(){
WebdriverHelper.openUrl("http://testsite/gamepage/");
ShortTestResult
testres = GenieScriptsExecutor.ExecuteScript("");
Assert.assertTrue(testres.getExecutionStatus().equals("Passed"),
testres.getMessage());
WebdriverHelper.waitforElementDisplayed(By.id("gameEnd"));
}
}