2018-06-10

How-to adapt test Data-pool.


 There is such a thing  Test Data-Pool. Seems to be usefull and there are usage examples but still it is not absolutely clear how-to start.
Given there are tests based on Java+testNG+maven + something else. Let's start:
1. Add maven dependency
 com.griddynamics.qa.datapool
 data-pool
 1.0.1
Specify actual version of course.
2. Greate/find entities package. Add a new entity class
package ru.test.automation.entities;

import com.griddynamics.qa.datapool.DataTypeFactory;
import com.griddynamics.qa.datapool.datatype.IDataType;

/**
 * Represents a BitBucketUser entity.
 */
public interface BitBucketUser extends IDataType {
    static BitBucketUser newUser(){
        return DataTypeFactory.create(BitBucketUser.class);
    }

    Integer getId();
    String getLogin();
    String getPassword();
}
Field set could be different.
3. Greate/find  /resources/data directory and add the data-file

!DataTypeKey 'interface ru.test.automation.entities.BitBucketUser':
- !IDataType {id: 1, login: Uzvr_Odin, password: Klu4slovo}
- !IDataType {id: 2, login: uzvr-Dva, password: derParrollen}
- !IDataType {id: 3, login: uzvr-Tri, password: AEtoKto?}

Let's name the file as ls.yml
4. Highly likely there is a BaseTest so add the following to it
import ru.test.automation.entities.BitBucketUser;
import com.griddynamics.qa.datapool.DataPool;

import static com.griddynamics.qa.datapool.FilterCriterion.by;
import static com.griddynamics.qa.datapool.matchers.StandardMatchers.CONTAINS;

...

@BeforeSuite
protected void loadData() {
    ClassLoader classLoader = getClass().getClassLoader();
    DataPool.load(Paths.get(classLoader.getResource("data/ls.yml").getPath()));
    // TODO: Switch to the approach below when Data-pool 1.0.2 is released.// DataPool.load("data/ls.yml");
}

//TODO: Think about moving into a separate data-access class.
protected BitBucketUser getUserByLogin(String partOfLoginName) {
    return DataPool.find(BitBucketUser.class, by("login", CONTAINS, partOfLoginName)).get(0);
}
5. And finally, within a test
BitBucketUser usr = getUserByLogin("Uzvr_Odin");
loginPage().login(usr.getLogin(), usr.getPassword());

Как перестать думать и прикрутить ДатаПул к тестам.

Есть вот такая штука - ДатаПул. Вроде бы полезная и примеры есть прямо в местележания, но как-то непонятно с чего начать прикручивать и как использовать.

Допустим есть тесты. Все из себя на Джаве + ТестНГ + мавен + что-нибудь ещё. Приступим:
1. Добавить зависимость
  
 com.griddynamics.qa.datapool
 data-pool
 1.0.1

Версию конечно же нужно проставить актуальную на текущий момент.

2. Создать/найти пакет entities (или что-то подобное) . Допуская, что нам нужны данные тестовых пользователей добавляем что-то вроде
package ru.test.automation.entities;

import com.griddynamics.qa.datapool.DataTypeFactory;
import com.griddynamics.qa.datapool.datatype.IDataType;

/**
 * Represents a BitBucketUser entity.
 */
public interface BitBucketUser extends IDataType {
    static BitBucketUser newUser(){
        return DataTypeFactory.create(BitBucketUser.class);
    }

    Integer getId();
    String getLogin();
    String getPassword();
}

Поля у объекта конечно же могут быть и не такими.

3. Создать/найти директорию /resources/data и добавить туда собственно файл с данными

!DataTypeKey 'interface ru.test.automation.entities.BitBucketUser':
!IDataType {id: 1, login: Uzvr_Odin, password: Klu4slovo}
!IDataType {id: 2, login: uzvr-Dva, password: derParrollen}
!IDataType {id: 3, login: uzvr-Tri, password: AEtoKto?}

Пусть этот файл зовётся ls.yml

4. Наверняка в тестах есть какой-нибудь BaseTest, туда можно-нужно-стоит добавить

import ru.test.automation.entities.BitBucketUser;
import com.griddynamics.qa.datapool.DataPool;

import static com.griddynamics.qa.datapool.FilterCriterion.by;
import static com.griddynamics.qa.datapool.matchers.StandardMatchers.CONTAINS;

...

@BeforeSuite
protected void loadData() {
    ClassLoader classLoader = getClass().getClassLoader();
    DataPool.load(Paths.get(classLoader.getResource("data/ls.yml").getPath()));
    // TODO: Switch to the approach below when Data-pool 1.0.2 is released.// DataPool.load("data/ls.yml");
}

//TODO: Think about moving into a separate data-access class.
protected BitBucketUser getUserByLogin(String partOfLoginName) {
    return DataPool.find(BitBucketUser.class, by("login", CONTAINS, partOfLoginName)).get(0);
}

5. И наконец! В нужном месте, в нужном тесте можно сделать так

BitBucketUser usr = getUserByLogin("Uzvr_Odin");
loginPage().login(usr.getLogin(), usr.getPassword());




2018-06-08

How do I get list of tests steps without executing the tests?

Given:
Auto-tests(Java + TestNG + Allure).
Steps (@Step) - return nothing, all are void.
There are data-driven tests.

The task:
a. Do not execute the steps;
b. Get list of all tests;
c. For each test - get list of steps with parameters' values.

A straightforward solution:
1. Use aspects (AspectJ);
2. Link @Around to ("anyMethod() && withStepAnnotation()");
3. Use org.aspectj.lang.reflect to get steps annotations and parameters names and values;
4. Inside the @Around advice - get the information needed but do not execute intercepted methods, just return null.

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.testng.ITestResult;
import org.testng.Reporter;
import ru.yandex.qatools.allure.annotations.Step;
...
@Pointcut("@annotation(ru.yandex.qatools.allure.annotations.Step)")
public void withStepAnnotation() {}

@Pointcut("execution( (..))")
public void anyMethod() {}
...
@Around("anyMethod() && withStepAnnotation()")
public Object logTestStepDataAndSkipExecution(ProceedingJoinPoint point) throws Throwable {
    if (isDryRun) {
        MethodSignature methodSignature = MethodSignature.class.cast(point.getSignature());

        String paramNamesAndValues = getParamNamesAndValues(point, methodSignature);
        
        Util.addStepDescriptionEntry(createTitle(point) + " | " + paramNamesAndValues);

        return null;
    } else {
        return point.proceed();
    }
}
...
private String getParamNamesAndValues(JoinPoint point, MethodSignature methodSignature) {
    String[] paramNames = methodSignature.getParameterNames();
    Object[] paramValues = point.getArgs();
    StringBuilder sb = new StringBuilder();
    for (int i=0; i< paramNames.length; i++) {
        sb.append(paramNames[i]).append("=").append(paramValues[i]).append(" | ");
    }
    return sb.toString();
}


Links:
Mainly inspired by  https://www.yegor256.com/2014/06/01/aop-aspectj-java-method-logging.html
Generally - https://duckduckgo.com/?q=Java+AspectJ+examples&t=ffab&ia=web

Как получить список шагов внутри теста без выполнения самих шагов?

Дано:
Авто-тесты(Джава + ТестНГ + Аллюр).
Шаги (@Step) - ничего не возвращают, все войд.
Среди тестов встречаются параметризированные.

Странная задача:
а. Сами тесты (шаги) не выполнять;
б. Получить список всех тестов;
в. Для каждого теста - получить список шагов вместе с параметрами.

Не менее странное влобное решение:
1. Использовать аспекты;
2. Вешать  @Around на ("anyMethod() && withStepAnnotation()");
3. Использовать org.aspectj.lang.reflect для вытаскивания аннотаций и названий/значений параметров;
4. Внутри совета @Around - вытягивать нужную информацию, а сам аллюровский шаг не запускать, возвращать налл.

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.testng.ITestResult;
import org.testng.Reporter;
import ru.yandex.qatools.allure.annotations.Step;
...
@Pointcut("@annotation(ru.yandex.qatools.allure.annotations.Step)")
public void withStepAnnotation() {}

@Pointcut("execution( (..))")
public void anyMethod() {}
...
@Around("anyMethod() && withStepAnnotation()")
public Object logTestStepDataAndSkipExecution(ProceedingJoinPoint point) throws Throwable {
    if (isDryRun) {
        MethodSignature methodSignature = MethodSignature.class.cast(point.getSignature());

        String paramNamesAndValues = getParamNamesAndValues(point, methodSignature);
        
        Util.addStepDescriptionEntry(createTitle(point) + " | " + paramNamesAndValues);

        return null;
    } else {
        return point.proceed();
    }
}
...
private String getParamNamesAndValues(JoinPoint point, MethodSignature methodSignature) {
    String[] paramNames = methodSignature.getParameterNames();
    Object[] paramValues = point.getArgs();
    StringBuilder sb = new StringBuilder();
    for (int i=0; i< paramNames.length; i++) {
        sb.append(paramNames[i]).append("=").append(paramValues[i]).append(" | ");
    }
    return sb.toString();
}


Ссылки:
Основная доля вдохновения почерпнута из https://www.yegor256.com/2014/06/01/aop-aspectj-java-method-logging.html
А в целом - https://duckduckgo.com/?q=Java+AspectJ+examples&t=ffab&ia=web

2018-05-18

О статусе релиза.

- What is the release status?
- "We have enough fails for now"(c).

Как оно, начинать.

 Начать автоматизировать/писать тесты и вообще начать похоже на езду на велосипеде. Вроде всё понятно и со стороны прелестно, а вот когда пробуешь сам, то просто не понимаешь как оно вообще возможно сохранить равновесие и куда-то ещё и ехать. И никакие тренинги, книжки, мастер-классы и прочее и прочее не  сделают за тебя тот самый первый толчок ногой и не проедут первые виляющие метры. Только сам, только набивая шишки.

2018-05-11

2018-01-23

О бардаке с управлением и рулением.

"Никакого бардака с рулением нет, у нас спонтанно-динамическая стабилизация по всем каналам управления с элементами роевой самоорганизации."(а).

2018-01-17

TestNG, skipped tests and "why" related information.


 As usually there are passed, failed and skipped tests. TestNG map skip tests due to failures in
precondition steps (i.e. somewhere inside all those @BeforeSuite/Group/Method routines). In such a case there is no quick-human-readable
explanation why a test was skipped. That is a pity, so what about the following:
1. Go to TestListenerAdapter#onTestSkipped(ITestResult result);
2. Check Throwable of that ITestResult;
3. If null then get skipped configurations - result.getTestContext().getSkippedConfigurations();
4. I haven't found a straight way to link skipped configuration and skipped test, but one could try to do that via TestContext/TestClass:
check whether skippedConfig.getTestClass().equals(result.getTestClass()))
5. If there is skipped config caused the test to be skipped then use result.setThrowable() to transfer throwable to ITestResult;
6. No guarantees but seems to work.

TestNG, пропущенные тесты и информация о причинах.


 Как общенеизвестно тесты после прогона бывают прошедшие, упавшие и пропущенные.
ТестНГ "пропускает" тесты в том числе и потому что что-то упало на этапе подготовки -
если где-то в глубинах @BeforeSuite/Group/Method что-то завалилось, то всё, гонять зависимые тесты уже нет смысла, сворачиваемся и идём обедать.
К сожалению в этом случае тесты хоть и помечаются "пропущенными", но без особого объяснения причин(лишь бодрое ничего приветствует любопытствующих), мол смотрите логи, может повезёт.

А хочется же подробностей и вот что придумалось:
1. TestListenerAdapter#onTestSkipped(ITestResult result);
2. Проверить Throwable в ITestResult;
3. Если налловое, то вытащить пропущенные/порушенные конфигурации через result.getTestContext().getSkippedConfigurations();
4. Способа связать упавший конфиг с конкретным тестом не нашёл, но можно сделать это через тестовый класс:
skippedConfig.getTestClass().equals(result.getTestClass()))
5. Если удалось связать, то через result.setThrowable() "перенести" исключения из конфигов в ITestResult
6. На практике даже вроде работает.

2018-01-11

Программное обновление статуса прогона тестов ТестЛинк.

 На проекте завёлся TestLink - жизнь стала насыщенее и встал вопрос хранения результатов прогона регрессии в том числе и в нём. Сохранять вручную конечно же не вариант, а посему нужно программное решение. И тут началось... .
 Задача - по результатам прогона тестов обновлять соответствующие сущности в ТестЛинке (создавать, удалять объекты не нужно, просто сохранять результаты прогона тестов на конкретной сборке.)
 Идея - использовать некий клиент для тестлинковского ПИП чтобы после прогона теста/тестов загружать результаты.
 Дробности решения:
1. яндексирование выявило лишь одну более-менее свежую реализацию - http://kinow.github.io/testlink-java-api/index.html
2. ПИП этот разрабатывал какой-то маньяк-ветеран. Методы, отсвечивающие стройными рядами входных параметров, прямо таки напомнили "святые 90-е", Win32API и Паскаль.
3. чтобы вызовы заработали нужно не только заиметь/сгенерировать ключ (http://forum.testlink.org/viewtopic.php?f=10&t=2858), но и кое-что включить в настройках сервера - https://github.com/parthibann/Python-TestLink-Runner/wiki/Enabling-Test-Automation-in-Testlink
4. если неизвестно что именно передавать в методы(см. выше), то можно смело пробовать налловые значения, часто помогает.
5. всё очень медленно.
6. "внешние" идентификаторы тестлинковских кейсов можно хранить в аллюровских @TestCaseId (связь тест<->тест_кейс). Вытащить данные из аннотаций достаточно легко. Под "внешними" имеется в виду идентификаторы вида ТА-1234, где ТА это аббревиатура названия проекта в ТестЛинке, а 1234 это порядковый номер кейса.
7. вполне можно делать всё в TestListenerAdapter#onFinish() (речь о TestNG):

URL testLinkURL = new URL(testLinkHost + "/lib/api/xmlrpc.php");
testLinkApi = new TestLinkAPI(testLinkURL, devKey);

report(testLinkApi, testPlan, testBuild, ExecutionStatus.NOT_RUN, 
        testContext.getSkippedTests().getAllMethods()); 

report(testLinkApi, testPlan, testBuild, ExecutionStatus.PASSED, 
        testContext.getPassedTests().getAllMethods()); 

report(testLinkApi, testPlan, testBuild, ExecutionStatus.FAILED, 
        testContext.getFailedTests().getAllMethods());

... 

private void reportResult(TestLinkAPI testLinkApi, TestPlan testPlan, 
                          Build testBuild, ExecutionStatus status, 
                          Collection testNgMethods) 

  for (ITestNGMethod testMethod : testNgMethods) {
    String testCaseId = getExternalIdFromAnnotations(testMethod, testMethod.getMethodName());
    for (String extId : testCaseIds) {                
      try {                    // 'null' - to return the latest version of test case                    
        TestCase testLinkCase = testLinkApi.getTestCaseByExternalId(extId, null);                    
        testLinkApi.reportTCResult(testLinkCase.getId(), null, testPlan.getId(),
                                status, testBuild.getId(), null, 
                                "A note on test case execution,", 
                                null, null, null, null, null, null);
        } catch (TestLinkAPIException e) { 
                           //Do something... .
         }
    }
  } 
 }
}

2018-01-09

О чайниках.

"Офисные хомячки"(тм) делятся на тех кто доливает чайник после использования и на тех кто нет.