Есть отличная удаленная работа для php+codeception+jenkins+allure+docker спецов. 100% remote! Присоединиться к проекту

Инициализация драйвера с необходимыми capabilities перед запуском теста


(heartwilltell) #1

Есть определенный набор тестов, который запускается под различными драйверами (сафари, хром, фф, ие), хотелось бы добавить к этому набору еще пару тестов, которые требуют определенные capabilities драйвера, не вырывая их из контекста данного джоба/сюита.

Есть ли возможность засетить capabilities драйверу прямо перед началом теста, а по его окончанию их убрать?

Читал - http://blog.qatools.ru/thucydides/thucydides-fixture-service/ - но не сильно понял как именно в этой ситуации создается драйвер.

И вообще какие есть способы, передачи драйверу capabilities в thucydides. Сейчас я делаю следующим образом:

public class iphoneUserAgentChromeDriver implements DriverSource {
    public WebDriver newDriver() {
        DesiredCapabilities desiredCapabilities = new DesiredCapabilities();
        ChromeOptions chromeOptions = new ChromeOptions();
        chromeOptions.addArguments("--user-agent=Mozilla/5.0 (iPhone; CPU iPhone OS 7_0_2 like Mac OS X) AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 Mobile/11A4449d Safari/9537.53");
        desiredCapabilities.setCapability(ChromeOptions.CAPABILITY, chromeOptions);
        return new ChromeDriver(desiredCapabilities);
    }

    public boolean takesScreenshots() {
        return true;
    }
}

При этом нужно создать проперти файл в котором указать следующее:

webdriver.driver = provided
webdriver.provided.type = iphoneUserAgentChromeDriver
webdriver.provided.iphoneUserAgentChromeDriver = MPSAutoTests.logic.iphoneUserAgentChromeDriver
thucydides.driver.capabilities = iphoneUserAgentChromeDriver

И при запуске мавену передавать путь к проперти файле -Dproperties=/путь и драйвер -Dwebdriver.drive=provided

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


Thucydides. Смена драйвера во время выполнения тестов из одной Story
(Sergey Korol) #2

Насколько я правильно понял, вначале вы имплементируете FixtureService интерфейс, затем в ресурсы засовываете файлик (с определенной иерархией каталогов), содержащий имя того самого класса, его реализующего. Ну а сам класс будет содержать переопределенный addCapabilitiesTo, из которого вы можете вытянуть капабилити текущей конфигурации драйвера, и затем подсунуть свои собственные. Минус в том, что вам еще нужно как-то проверить условия добавления этих самых капабилити, т.е. имя теста или что там у вас еще. Помимо всего прочего, как пишут очевидцы, addCapabilityTo еще и 2 раза вызывается, т.е. этот момент тоже нужно будет как-то хэндлить. Можно конечно попробовать заюзать AOP, перехватив вызов этого метода и подсунув ему еще и кастомный параметр критерия инжекта капабилити. Хотя, по идее с AOP вообще можно все вместе пропихнуть по заданному условию. Ну или хранить условия в паблик статик доступе, чтобы можно было вычитать в момент обращения к методу. Хотя, пока еще не ясно когда именно вызывается этот метод. Возможно информации о тесте то и не будет существовать в момент вызова.


(heartwilltell) #3

Что-то уж больно страшно это все звучит, я конечно покопаю, но боюсь разобраться в этом на моем уровне знания языка пока не представляется возможным.


(Sergey Korol) #4

Начните с малого. Создайте класс, который имплементирует FixtureService. Методы могут быть пустыми. В addCapabilitiesTo добавьте логирование, чтобы определить приблизительный момент вызова. Попробуйте еще дернуть какую-то shared переменную из этого метода. Это даст вам понять, насколько легко / сложно будет контролировать капабилити из переопределенного метода. Должно выглядеть приблизительно так:

@Override
public void addCapabilitiesTo(DesiredCapabilities capabilities) {
        if (capabilities.getBrowserName().equals("chrome") && yourCondition) {
            ChromeOptions chromeOptions = new ChromeOptions();
            chromeOptions.addArguments("--user-agent=Mozilla/5.0 (iPhone; CPU iPhone OS 7_0_2 like Mac OS X) AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 Mobile/11A4449d Safari/9537.53");
            capabilities.setCapability(ChromeOptions.CAPABILITY, chromeOptions);
        }
    }

При этом yourCondition будет неким shared объектом.

Об эксперименте с AOP я писал в этой статье. Т.е. в принципе, вы можете попробовать вытянуть капабилити параметр из addCapabilitiesTo и засунуть туда ваши собственные, еще до непосредственного вызова метода.


(heartwilltell) #5

Спасибо буду пробовать


(Pnevmoslon) #6

Делал подобное у себя на проекте. @ArtOfLife всё правильно сказал :slight_smile: Если не будет получаться - спрашивайте. Возможно смогу помочь


(heartwilltell) #7

Совсем не понимаю как задать условие при котором выполнятся это будет только для определенного теста.
Это нужно делать как-то с помощью рефлексии?


(Sergey Korol) #8

Наиболее простым способом наверное будет связка кастомной аннотации и перехватчика методов.

Создаем аннотацию:

@Retention(RetentionPolicy.RUNTIME)
@Target(value = { ElementType.METHOD, ElementType.TYPE })
public @interface CustomCapability {
}

Далее аннотируем тестовый метод, которому надо подсунуть кастомные капабилити.

@CustomCapability
@Test
public void someTest() {
    // your steps
}

Теперь нам нужно как-то определить, что у теста есть кастомная аннотация. Сильно не углубляясь в дебри JUnit и Thucydides, нашел 2 способа:

  • Используя JUnit правило TestRule. Причем, вариант с вынесением кода в какой-нибудь BaseTest посредством наследования тоже прокатит.
public TestRule watcher = new TestWatcher() {
    protected void starting(Description description) {
        Injectors.getInjector()
                .getInstance(EnvironmentVariables.class)
                .setProperty(CustomFixture.USE_CUSTOM_CAPABILITIES,
                        String.valueOf(description.getAnnotation(CustomCapability.class) != null));
    }
};
  • Расширив ThucydidesRunner, переопределив methodBlock. Собственно родной ThucydidesRunner.class придется заменить в RunWith аннотации на наш расширенный раннер.
@Override
protected Statement methodBlock(FrameworkMethod method) {
    Injectors.getInjector()
            .getInstance(EnvironmentVariables.class)
            .setProperty(CustomFixture.USE_CUSTOM_CAPABILITIES,
                    String.valueOf(method.getAnnotation(CustomCapability.class) != null));

    return super.methodBlock(method);
}
@RunWith(CustomRunner.class)
public class YourTests {
    // ...
}

В обоих случаях мы получаем доступ к текущему тестовому методу, что дает нам возможность проверить наличие кастомной аннотации и засетить какой-нибудь кастомный проперти в EnvironmentVariables.class при помощи Guice инжектора.

Ну а дальше - дело техники. На текущем этапе мы уверены, что некий USE_CUSTOM_CAPABILITIES проперти имеет значение true в случае наличия аннотации, и false - ввиду ее отсутствия.

Теперь в переопределенном addCapabilitiesTo достаточно проверить значение нашего проперти и засетить кастомные капабилити.

public class CustomFixture implements FixtureService {

    public static final String USE_CUSTOM_CAPABILITIES = "use.custom.capabilities.arg";

    public void setup() {
    }

    public void shutdown() {
    }

    public void addCapabilitiesTo(DesiredCapabilities capabilities) {
        if (Injectors.getInjector().getInstance(EnvironmentVariables.class)
                .getPropertyAsBoolean(USE_CUSTOM_CAPABILITIES, false)) {

            System.out.println("Setting custom capabilities...");
        }
    }
}

Ну а при помощи AspectJ можно осуществить перехват следующим образом:

@Aspect
public class CustomCapabilitiesInterceptor {
    @Before("execution(* your.tests.package..*.*(..))")
    public void beforeInvocation(final JoinPoint joinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        String methodName = signature.getMethod().getName();
        Class<?>[] parameterTypes = signature.getMethod().getParameterTypes();

        Injectors.getInjector()
                .getInstance(EnvironmentVariables.class)
                .setProperty(CustomFixture.USE_CUSTOM_CAPABILITIES,
                        String.valueOf(joinPoint.getTarget()
                                .getClass()
                                .getMethod(methodName,parameterTypes)
                                .getAnnotation(CustomCapability.class) != null));
    }
}

You’re welcome. :wink:


(heartwilltell) #9

Ох :smile: пошел фигачить, обязательно отпишусь по результатам