Java 无法在JBehave中具有公共生命周期步骤的不同故事之间共享web驱动程序实例

Java 无法在JBehave中具有公共生命周期步骤的不同故事之间共享web驱动程序实例,java,jbehave,Java,Jbehave,我有两个不同的步骤类,它们在步骤之前和步骤之后具有共同的生命周期。因此,我创建了一个通用的step类,该类将webdriver初始化和清理代码作为方法前后的生命周期。该公共类由两个步骤定义类扩展。在运行这些故事时,我在第二步类中访问web驱动程序时看到空指针异常。进一步调试我注意到,每当故事文件中出现场景时,都会使用first step类对象调用公共步骤 我的故事档案1 Meta: @author prasanta biswas @theme UI Test Narrative: As a F

我有两个不同的步骤类,它们在步骤之前和步骤之后具有共同的生命周期。因此,我创建了一个通用的step类,该类将webdriver初始化和清理代码作为方法前后的生命周期。该公共类由两个步骤定义类扩展。在运行这些故事时,我在第二步类中访问web驱动程序时看到空指针异常。进一步调试我注意到,每当故事文件中出现场景时,都会使用first step类对象调用公共步骤

我的故事档案1

Meta:
@author prasanta biswas
@theme UI Test

Narrative:
As a Facebook User
In order to update my status
I want to login to my Facebook account
So that I can update my status message

Lifecycle:
Before:
Scope: SCENARIO
Given I initialize my chrome browser instance
After:
Scope: SCENARIO|STORY
Outcome: ANY
Given I cleanup my browser instance

Scenario: Update Facebook status message
Meta:
@type positive
@data valid
@app facebook

Given I have a Facebook account
And I open Facebook in my web browser with the <url>
And I login to Facebook with <username> and <password>
Then I check if status message box is present in the homepage after successful login
When I put my status <message> in the status box
And I click post button
Then I should see my new status <message> in my news feeds

Examples:
|username|password|url|message|
|VALID_USERNAME|VALID_PASSWORD|https://www.facebook.com|Hi Facebook|
我的第一步定义类:

package io.jbehavetutorials.steps.facebook;

import io.jbehavetutorials.appmodule.facebook.FacebookApp;
import io.jbehavetutorials.constants.Browser;
import io.jbehavetutorials.steps.common.CommonSteps;
import io.jbehavetutorials.testutility.BrowserFactory;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jbehave.core.annotations.*;
import org.junit.Assert;
import org.openqa.selenium.WebDriver;

import java.net.MalformedURLException;

/**
 * Created by prasantabiswas on 21/05/18.
 */
public class FacebookStatusPost extends CommonSteps{
    FacebookApp facebookApp;
    Logger logger = LogManager.getLogger(FacebookStatusPost.class);

    @Given("I have a Facebook account")
    @Pending
    public void iHaveAFacebookAccount()
    {
        try {
            facebookApp = new FacebookApp(webDriver); // Working perfectly here
            Assert.assertTrue("Facebook object creation failed",facebookApp!=null);
        }
        catch (Exception e)
        {
            logger.error("Error",e);
        }
    }
}
package io.jbehavetutorials.steps.wallethub;

import io.jbehavetutorials.appmodule.wallethub.WalletHubApp;
import io.jbehavetutorials.constants.Browser;
import io.jbehavetutorials.steps.common.CommonSteps;
import io.jbehavetutorials.testutility.BrowserFactory;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jbehave.core.annotations.*;
import org.junit.Assert;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.support.ui.ExpectedConditions;

import java.net.MalformedURLException;

/**
 * Created by prasantabiswas on 21/05/18.
 */
public class WalletHubReviewInsurance extends CommonSteps {
    WalletHubApp wallethubApp;
    int reviewCount = 0;
    Logger logger = LogManager.getLogger(WalletHubReviewInsurance.class);

    @Given("I have a WalletHub test account")
    public void iHaveAWallerHubTestAccount()
    {
        try{
            wallethubApp = new WalletHubApp(webDriver);//Getting null pointer here
        }
        catch (Exception e)
        {
            logger.error("Error",e);
        }
    }
}
我的第二步定义类:

package io.jbehavetutorials.steps.facebook;

import io.jbehavetutorials.appmodule.facebook.FacebookApp;
import io.jbehavetutorials.constants.Browser;
import io.jbehavetutorials.steps.common.CommonSteps;
import io.jbehavetutorials.testutility.BrowserFactory;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jbehave.core.annotations.*;
import org.junit.Assert;
import org.openqa.selenium.WebDriver;

import java.net.MalformedURLException;

/**
 * Created by prasantabiswas on 21/05/18.
 */
public class FacebookStatusPost extends CommonSteps{
    FacebookApp facebookApp;
    Logger logger = LogManager.getLogger(FacebookStatusPost.class);

    @Given("I have a Facebook account")
    @Pending
    public void iHaveAFacebookAccount()
    {
        try {
            facebookApp = new FacebookApp(webDriver); // Working perfectly here
            Assert.assertTrue("Facebook object creation failed",facebookApp!=null);
        }
        catch (Exception e)
        {
            logger.error("Error",e);
        }
    }
}
package io.jbehavetutorials.steps.wallethub;

import io.jbehavetutorials.appmodule.wallethub.WalletHubApp;
import io.jbehavetutorials.constants.Browser;
import io.jbehavetutorials.steps.common.CommonSteps;
import io.jbehavetutorials.testutility.BrowserFactory;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jbehave.core.annotations.*;
import org.junit.Assert;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.support.ui.ExpectedConditions;

import java.net.MalformedURLException;

/**
 * Created by prasantabiswas on 21/05/18.
 */
public class WalletHubReviewInsurance extends CommonSteps {
    WalletHubApp wallethubApp;
    int reviewCount = 0;
    Logger logger = LogManager.getLogger(WalletHubReviewInsurance.class);

    @Given("I have a WalletHub test account")
    public void iHaveAWallerHubTestAccount()
    {
        try{
            wallethubApp = new WalletHubApp(webDriver);//Getting null pointer here
        }
        catch (Exception e)
        {
            logger.error("Error",e);
        }
    }
}
故事地图类:

package io.jbehavetutorials;

import io.jbehavetutorials.steps.facebook.FacebookStatusPost;
import io.jbehavetutorials.steps.wallethub.WalletHubReviewInsurance;
import io.jbehavetutorials.steps.weatherapi.WeatherAPITest;
import org.jbehave.core.embedder.StoryControls;
import org.jbehave.core.junit.JUnitStories;

import java.text.SimpleDateFormat;
import java.util.List;

import org.jbehave.core.Embeddable;
import org.jbehave.core.configuration.Configuration;
import org.jbehave.core.configuration.MostUsefulConfiguration;
import org.jbehave.core.i18n.LocalizedKeywords;
import org.jbehave.core.io.CodeLocations;
import org.jbehave.core.io.LoadFromClasspath;
import org.jbehave.core.io.StoryFinder;
import org.jbehave.core.model.ExamplesTableFactory;
import org.jbehave.core.model.TableTransformers;
import org.jbehave.core.parsers.RegexStoryParser;
import org.jbehave.core.reporters.StoryReporterBuilder;
import org.jbehave.core.reporters.SurefireReporter;
import org.jbehave.core.steps.*;
import org.jbehave.core.steps.ParameterConverters.DateConverter;
import org.jbehave.core.steps.ParameterConverters.ExamplesTableConverter;

import static org.jbehave.core.io.CodeLocations.codeLocationFromClass;
import static org.jbehave.core.reporters.Format.CONSOLE;
import static org.jbehave.core.reporters.Format.HTML;
import static org.jbehave.core.reporters.Format.TXT;
import static org.jbehave.core.reporters.Format.XML;

/**
 * <p>
 * {@link Embeddable} class to run multiple textual stories via JUnit.
 * </p>
 * <p>
 * Stories are specified in classpath and correspondingly the {@link LoadFromClasspath} story loader is configured.
 * </p> 
 */
public class StoryMap extends JUnitStories {

    public StoryMap() {
        configuredEmbedder().embedderControls().doGenerateViewAfterStories(true).doIgnoreFailureInStories(true)
                .doIgnoreFailureInView(true).useThreads(1);
//        configuredEmbedder().useMetaFilters(Arrays.asList("+type negative"));
    }

    @Override
    public Configuration configuration() {
        Class<? extends Embeddable> embeddableClass = this.getClass();
        // Start from default ParameterConverters instance
        ParameterConverters parameterConverters = new ParameterConverters();
        // Start from default ParameterControls instance
        ParameterControls parameterControls = new ParameterControls();
        // factory to allow parameter conversion and loading from external resources (used by StoryParser too)
        ExamplesTableFactory examplesTableFactory = new ExamplesTableFactory(new LocalizedKeywords(), new LoadFromClasspath(embeddableClass),parameterConverters,parameterControls, new TableTransformers());
        // add custom converters
        parameterConverters.addConverters(new DateConverter(new SimpleDateFormat("yyyy-MM-dd")),
                new ExamplesTableConverter(examplesTableFactory));
        return new MostUsefulConfiguration()
                //.usePendingStepStrategy(new FailingUponPendingStep())
                .useStoryControls(new StoryControls().doDryRun(false))
                .useStoryLoader(new LoadFromClasspath(embeddableClass))
                .useStoryParser(new RegexStoryParser(examplesTableFactory))
//                .useStoryParser(new GherkinStoryParser())
                .useStoryReporterBuilder(new StoryReporterBuilder()
                .withSurefireReporter(new SurefireReporter(embeddableClass))
                    .withCodeLocation(CodeLocations.codeLocationFromClass(embeddableClass))
                    .withDefaultFormats()
                    .withFormats(CONSOLE, TXT, HTML, XML)
                    .withFailureTrace(true)
                    .withFailureTraceCompression(true))
            .useParameterConverters(parameterConverters)
            .useParameterControls(parameterControls);
    }

    @Override
    public InjectableStepsFactory stepsFactory() {
        return new InstanceStepsFactory(configuration(), new FacebookStatusPost(), new WalletHubReviewInsurance(), new WeatherAPITest());
//        return new ScanningStepsFactory(configuration(),"io.jbehavetutorials.steps"); //requires reflection dependency
    }

    @Override
    protected List<String> storyPaths() {
        return new StoryFinder().findPaths(codeLocationFromClass(this.getClass()), "**/*.story", "**/excluded*.story");

    }

}
package io.jbehave教程;
导入io.jbehavetutorials.steps.facebook.FacebookStatusPost;
导入io.jbehavetutorials.steps.wallethub.WalletHubReviewInsurance;
导入io.jbehavetutorials.steps.weatherapi.WeatherAPITest;
导入org.jbehave.core.embedder.StoryControls;
导入org.jbehave.core.junit.JUnitStories;
导入java.text.simpleDataFormat;
导入java.util.List;
导入org.jbehave.core.embeddeble;
导入org.jbehave.core.configuration.configuration;
导入org.jbehave.core.configuration.mostuseveConfiguration;
导入org.jbehave.core.i18n.LocalizedKeywords;
导入org.jbehave.core.io.CodeLocations;
导入org.jbehave.core.io.LoadFromClasspath;
导入org.jbehave.core.io.StoryFinder;
导入org.jbehave.core.model.ExamplesTableFactory;
导入org.jbehave.core.model.TableTransformers;
导入org.jbehave.core.parsers.RegexStoryParser;
导入org.jbehave.core.reporters.StoryReporterBuilder;
导入org.jbehave.core.reporters.SurefireReporter;
导入org.jbehave.core.steps.*;
导入org.jbehave.core.steps.ParameterConverters.DateConverter;
导入org.jbehave.core.steps.ParameterConverter.ExamplesTableConverter;
导入静态org.jbehave.core.io.CodeLocations.codeLocationFromClass;
导入静态org.jbehave.core.reporters.Format.CONSOLE;
导入静态org.jbehave.core.reporters.Format.HTML;
导入静态org.jbehave.core.reporters.Format.TXT;
导入静态org.jbehave.core.reporters.Format.XML;
/**
*
*{@link embeddeble}类通过JUnit运行多个文本故事。
*

* *故事在类路径中指定,并相应地配置了{@link LoadFromClasspath}故事加载器。 *

*/ 公共类故事图扩展了JUnitStores{ 公共故事地图(){ configuredEmbedder().embedderControls().doGenerateViewAfterStories(true).doIgnoreFailureInStories(true) .doIgnoreFailureInView(true).useThreads(1); //configuredEmbedder().useMetaFilters(Arrays.asList(“+type negative”); } @凌驾 公共配置(){
类一个解决方法是使驱动程序实例
CommonSteps
类中保持静态。这不是一个具体的解决方案,因为它不能与多个线程同时运行故事一起使用,因为它会使驱动程序实例易受攻击。

问题是扩展
CommonSteps
的每个类都有自己的insta您应该为webDriver实例创建一些存储,这些存储将在所有步骤实现中共享。但这不是一个错误。它应该为每个步骤类创建单独的对象来调用其父类方法。但是,它被其他类对象调用只是为了调用生命周期方法。不,这不是一个bug。这些是OOP的基本原则,您可以在没有JBehave的情况下模拟相同的行为:父类状态不会在子类之间共享,除非它是静态的。这不是关于共享状态,而是从其子类调用父类init函数,以便每个子对象都有自己的状态。例如,FacebookStatusPost和WalletHubReviewInsurance类应分别从各自的实例中调用CommonSteps中的setUp和tearDown方法,以便每个对象都可以使用其自己初始化的webDriver实例。但在JBehave中,这是不同的。对于两个子类步骤,父类方法仅从其一个子类的对象调用s执行。因此webDriver对于其他步骤类对象为空。您声明了两个步骤类实例,它们声明了相同的步骤:
new FacebookStatusPost(),new WalletHubReviewInsurance()
。当您运行场景时,将调用第一个匹配的步骤。在您的示例步骤中(给定我初始化我的chrome浏览器实例)始终从
FacebookStatusPost
调用,因此此实例中的
webDriver
始终被初始化,但从不从
WalletHubReviewInsurance