jbehave run only specific story - selenium

I have jbehave integrated with Selenium. I am running my tests through command line as below
C:\eclipse_workspace\MySeleniumTests>mvn clean test -Dwebdriver.firefox.bin="C:\Program Files\Mozilla\Firefox\firefox.exe"
I have used jbehave-maven-plugin. Maven picks up all the Embedder impl (JunitStories in my case) from the source directory and execute them one by one. Configuration for that is <include>**/*Stories.java</include> in pom.xml
It then looks for relevant .story files in the specified dir and executes them. Say, I have two story files one.story and two.story, both of them are executed.
Over a time, number of story files are going to increase I only want to execute specific story files should there be a way to do this? I am thinking to pass specific story file names as run time parameters but don’t know what is required to make that happen.

I got it working with the below code
mvn clean test -Dwebdriver.firefox.bin="C:\Program Files\Mozilla\Firefox\firefox.exe" -Dstory=myStory.story
Override storyPaths() method in embedder class as below.
public class MyTestStories extends JUnitStories /* InjectableEmbedder */{
#Override
protected List<String> storyPaths() {
List<String> storiesToRun = new ArrayList<String>();
String storyProperty = System.getProperty("story");
if (storyProperty == null || storyProperty.isEmpty()) {
throw new RuntimeException("Please specify which stories to run");
}
String[] storyNames = storyProperty.split(",");
StoryFinder sf = new StoryFinder();
URL baseUrl = CodeLocations.codeLocationFromClass(this.getClass());
for (String storyName : storyNames) {
storiesToRun.addAll(sf.findPaths(baseUrl, storyName, ""));
}
return storiesToRun;
}

Try the following:
mvn clean test -Dwebdriver.firefox.bin="C:\Program Files\Mozilla\Firefox\firefox.exe" -Djbehave.story.name=<story filename without extension (wildcards are supported)>
You should also use custom test suite implementation:
public abstract class JBehaveTestSuite extends ThucydidesJUnitStories {
private static final String STORY_NAME_PATTERN = "**/${jbehave.story.name:*}.story";
public JBehaveTestSuite() {
findStoriesCalled(storyNamesFromEnvironmentVariable());
}
#Override
public void run() throws Throwable {
super.run();
}
private String storyNamesFromEnvironmentVariable() {
return SystemPropertyUtils.resolvePlaceholders(STORY_NAME_PATTERN);
}
}

Related

How to create a Gradle task of type KotlinCompile

I'm trying to compile generated Kotlin source code in a custom location to a custom location so that I can build a jar file with those class file only.
I had no issues setting it up for Java. Unfortunately, I'm having problems with Kotlin.
So here is the Kotlin version of what worked for me in Java:
public class MyCustomPlugin implements Plugin<Project> {
private static final String GENERATED_STUB_CLASSES_DIRECTORY = "generated-stub-classes";
private static final String GENERATED_STUB_SOURCES_DIRECTORY = "generated-stub-sources";
public void apply(Project project) {
project.getPluginManager().apply(KotlinPluginWrapper.class);
createCompileStubsTask(project);
}
private void createCompileStubsTask(final Project project) {
KotlinCompile compileKotlin = (KotlinCompile) project.getRootProject().getTasksByName("compileKotlin", true).iterator().next();
TaskProvider<KotlinCompile> compileKotlinStubs = project.getTasks().register("compileStubs", KotlinCompile.class,
compileStubs -> {
File stubsClassesDir = new File(project.getBuildDir() + "/" + GENERATED_STUB_CLASSES_DIRECTORY);
stubsClassesDir.mkdirs();
compileStubs.setClasspath(compileKotlin.getClasspath());
compileStubs.source(project.getLayout().getBuildDirectory().dir(GENERATED_STUB_SOURCES_DIRECTORY));
compileStubs.getDestinationDirectory().set(stubsClassesDir);
});
compileKotlin.finalizedBy(compileKotlinStubs);
}
}
This fails with:
Unable to determine constructor argument #1: missing parameter of type KotlinJvmOptions, or no service of type KotlinJvmOptions.
I tried to do it in the build.gradle file, like this:
task compileStubs(type: org.jetbrains.kotlin.gradle.tasks.KotlinCompile) {
File stubsClassesDir = new File(project.getBuildDir().name + "/generated-stub-classes")
stubsClassesDir.mkdirs()
compileStubs.setClasspath(compileKotlin.getClasspath())
compileStubs.source(project.getLayout().getBuildDirectory().dir("generated-stub-sources"))
compileStubs.getDestinationDirectory().set(stubsClassesDir)
}
compileKotlin.finalizedBy(compileKotlinStubs)
But the result is exactly the same.
Please help...

Pass dynamic value to test method parameter using TestNG class

I am automating a web page which runs in multi-threading environment, so I am exporting every test result into a file system and I wanted to maintain every test result uniquely for the future reference. So is there a way to pass file name as parameter to a test method dynamically while calling it from TestNG class.
I know we can pass parameters from .xml file but if I do that the values will more like static and can be seen by all the thread running parallel.
Test class will be called from main method as bellow
public class Test {
public static void main(String[] args) throws ParseException {
try
{
TestNG testng = new TestNG();
testng.setTestClasses(new Class[] { Testing.class });
testng.run();
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
Bellow code is my test method
public class Testing {
#Test
#Parameters("filename")
public void testMethod(String fileName){
System.out.println("filename is: "+fileName);
// ---- remaining test logic -----
}
}
Or can we use TestListenerAdapter onStart() method to inject parameter values...?.
If you want unique file name you can just add it a time stamp
Date date = new Date();
Format formatter = new SimpleDateFormat("yyyy-MM-dd_HH:mm:ss");
String timeStamp = formatter.format(date);
String fileName = "TestResults-" + timeStamp;
You can store your values into ITestContext which will be available for all tests.
You can set up the values in a configuration method (#BeforeSuite for example) or a listener.
Pass Dynamic Parameters to TestNG suite during runtime
What the below code does:
I want to add a list of parameters to each test during runtime. These parameters are passed as maven runtime arguments. They are read using System.getProperty() method as shown below. Then these parameters are added to the test inside suite and testng is ran successfully. This can be really useful in other scenarios as well.
The below code reads the testng.xml file and adds parameter to
List<String> parameters = new ArrayList<>();
parameters = Arrays.asList(System.getProperty("parameters").split(",");
TestNG tng = new TestNG();
File initialFile = new File("testng.xml");
InputStream inputStream = FileUtils.openInputStream(initialFile);
Parser p = new Parser(inputStream);
List<XmlSuite> suites = p.parseToList();
for(XmlSuite suite:suites){
List<XmlTest> tests = suite.getTests();
for (XmlTest test : tests) {
for (int i = 0; i < parameters.size(); i++) {
HashMap<String, String> parametersMap = new HashMap<>();
parametersMap.put("parameter",parameters.get(i));
test.setParameters(parametersMap);
}
}
}
tng.setXmlSuites(suites);
tng.run();

How do I replace one test in an XML-defined suite with three others in TestNG?

Our team uses TestNG to run some tests in Selenium. We need to be able to run a given test on 3 different browsers (Chrome, Firefox, and [sadly] IE). We have a browser parameter on our base test class and really we could just declare three tests, one each for each browser; however, we'd really like to just be able to specify the browser value as "Standard 3" and have that run the test on each browser automatically.
So, I've built a class that implements ISuiteListener and attempts to create the new tests on the fly. However, any way I try to add tests fails. That is, no new tests I try to add will be executed by the suite. It's like nothing I did actually changed anything.
Here's my code:
public class Standard3BrowserSuiteListener implements ISuiteListener {
#Override
public void onStart(final ISuite suite) {
final XmlSuite xmlSuite = suite.getXmlSuite();
final Map<String, String> suiteParameters = xmlSuite.getParameters();
final List<XmlTest> currentTests = new ArrayList<XmlTest>(xmlSuite.getTests());
final ArrayList<XmlTest> testsToRun = new ArrayList<XmlTest>(currentTests.size());
for (final XmlTest test : currentTests) {
final Browser browser;
final Map<String, String> testParameters = test.getAllParameters();
{
String browserParameter = testParameters.get("browser");
if (browserParameter == null) {
browserParameter = suiteParameters.get("browser");
}
browser = Util.Enums.getEnumValueByName(browserParameter, Browser.class);
}
if (browser == Browser.STANDARD_3) {
XmlTest nextTest = cloneTestAndSetNameAndBrowser(xmlSuite, test, testParameters, "Chrome");
xmlSuite.addTest(nextTest);
testsToRun.add(nextTest); // alternate I've tried to no avail
nextTest = cloneTestAndSetNameAndBrowser(xmlSuite, test, testParameters, "Firefox");
xmlSuite.addTest(nextTest);
testsToRun.add(nextTest); // alternate I've tried to no avail
nextTest = cloneTestAndSetNameAndBrowser(xmlSuite, test, testParameters, "IE");
xmlSuite.addTest(nextTest);
testsToRun.add(nextTest); // alternate I've tried to no avail
} else {
testsToRun.add(test);
}
}
// alternate to xmlSuite.addTest I've tried to no avail
testsToRun.trimToSize();
currentTests = xmlSuite.getTests();
currentTests.clear();
currentTests.addAll(testsToRun);
}
private XmlTest cloneTestAndSetNameAndBrowser(final XmlSuite xmlSuite, final XmlTest test,
final Map<String, String> testParameters, final String browserName) {
final XmlTest nextTest = (XmlTest) test.clone();
final Map<String, String> nextParameters = new TreeMap<String, String>(testParameters);
nextParameters.put("browser", browserName.toUpperCase());
nextTest.setName(browserName);
final List<XmlClass> testClasses = new ArrayList<XmlClass>(test.getClasses());
nextTest.setClasses(testClasses);
return nextTest;
}
#Override
public void onFinish(final ISuite suite) {}
}
How can I replace the test with the browser value "Standard 3" with 3 tests and have it run properly? Thanks!
Here's what you need to do :
Upgrade to the latest released version of TestNG.
Build an implementation of org.testng.IAlterSuiteListener
Move your implementation that you created in ISuiteListener into this listener implementation.
Wire in this listener via the <listeners> tag in your suite XML File (or) via ServiceLoaders (As described in the javadocs of this interface)

Combining dataproviders TestNG

I have read a few stackoverflow posts about combining dataproviders but I cant't get anything to work.
What I'm currently doing is a selenium test that takes screenshots of every language the site is translated to.
It simply clicks through every link while taking screenshots of it, then it switches the URL to another language and repeat.
My problem is when doing this I can't redirect my screenshots to a specific folder per "language test". To do this I need a second dataprovider, but I already have a dataprovider for this test method for running a different URL per test.
So I need to combine these two dataproviders somehow.
They currently look like this
public static Object [][] language(){
return new Object[][]{
{"https://admin-t1.taxicaller.net/login/admin.php?lang=en"},
{"https://admin-t1.taxicaller.net/login/admin.php?lang=sv"},
};
}
public static Object [][] directory(){
return new Object[][]{
{"screenshotsEnglish.dir"},
{"screenshotsSwedish.dir"},
};
}
In my test class I just want to reach these two by writing
driver.get(**url**);
// This is the screenshot method. Where "Directory" is written I decide where to save the screenshots
Properties settings = PropertiesLoader.fromResource("settings.properties");
String screenshotDir = settings.getProperty(**directory**);
screenShooter = new ScreenShooter(driver, screenshotDir, "en");
Hope I have made myself clear, appreciate all help!
Regards
public static Object[][] dp() {
return new Object[][]{
{
"https://admin-t1.taxicaller.net/login/admin.php?lang=en",
"screenshotsEnglish.dir"
},
{
"https://admin-t1.taxicaller.net/login/admin.php?lang=sv",
"screenshotsSwedish.dir"
}
};
}
#Test(dataProvider = "dp")
public void t(String url, String directory) {
driver.get(url);
Properties settings = PropertiesLoader.fromResource("settings.properties");
String screenshotDir = settings.getProperty(directory);
screenShooter = new ScreenShooter(driver, screenshotDir, "en");
/*...*/
}

Adding 'GO' statements to Entity Framework migrations

So I have an application with a ton of migrations made by Entity framework.
We want to get a script for all the migrations at once and using the -Script tag does work fine.
However...it does not add GO statements in the SQL giving us problems like Alter view should be the first statement in a batch file...
I have been searching around and manually adding Sql("GO"); help with this problem but only for the entire script. When I use the package console manager again it returns an exception.
System.Data.SqlClient.SqlException (0x80131904): Could not find stored procedure 'GO'.
Is there a way to add these GO tags only when using the -Script tag?
If not, what is a good approach for this?
Note: we have also tried having multiple files but since we have so many migrations, this is near impossible to maintain every time.
If you are trying to alter your view using Sql("Alter View dbo.Foos As etc"), then you can avoid the should be the first statement in a batch file error without adding GO statements by putting the sql inside an EXEC command:
Sql("EXEC('Alter View dbo.Foos As etc')")
In order to change the SQL Generated by entity framework migrations you can create a new SqlServerMigrationSqlGenerator
We have done this to add a GO statement before and after the migration history:
public class MigrationScriptBuilder: SqlServerMigrationSqlGenerator
{
protected override void Generate(System.Data.Entity.Migrations.Model.InsertHistoryOperation insertHistoryOperation)
{
Statement("GO");
base.Generate(insertHistoryOperation);
Statement("GO");
}
}
then add in the Configuration constructor (in the Migrations folder of the project where you DbContext is) so that it uses this new sql generator:
[...]
internal sealed class Configuration : DbMigrationsConfiguration<PMA.Dal.PmaContext>
{
public Configuration()
{
SetSqlGenerator("System.Data.SqlClient", new MigrationScriptBuilder());
AutomaticMigrationsEnabled = false;
}
[...]
So now when you generate a script using the -Script tag, you can see that the insert into [__MigrationHistory] is surrounded by GO
Alternatively in your implementation of SqlServerMigrationSqlGenerator you can override any part of the script generation, the InsertHistoryOperation was suitable for us.
Turn out the concept exist deep in the SqlServerMigrationSqlGenerator as an optional argument for Statement(sql, batchTerminator). Here is something based on Skyp idea. It works both in -script mode or not. The GOs are for different operations than for Skyp only because our needs are a little different. You then need to register this class in the Configuration as per Skyp instructions.
public class MigrationScriptBuilder : SqlServerMigrationSqlGenerator
{
private string Marker = Guid.NewGuid().ToString(); //To cheat on the check null or empty of the base generator
protected override void Generate(AlterProcedureOperation alterProcedureOperation)
{
SqlGo();
base.Generate(alterProcedureOperation);
SqlGo();
}
protected override void Generate(CreateProcedureOperation createProcedureOperation)
{
SqlGo();
base.Generate(createProcedureOperation);
SqlGo();
}
protected override void Generate(SqlOperation sqlOperation)
{
SqlGo();
base.Generate(sqlOperation);
}
private void SqlGo()
{
Statement(Marker, batchTerminator: "GO");
}
public override IEnumerable<MigrationStatement> Generate(IEnumerable<MigrationOperation> migrationOperations, string providerManifestToken)
{
var result = new List<MigrationStatement>();
var statements = base.Generate(migrationOperations, providerManifestToken);
bool pendingBatchTerminator = false;
foreach (var item in statements)
{
if(item.Sql == Marker && item.BatchTerminator == "GO")
{
pendingBatchTerminator = true;
}
else
{
if(pendingBatchTerminator)
{
item.BatchTerminator = "GO";
pendingBatchTerminator = false;
}
result.Add(item);
}
}
return result;
}
}
The easiest way is to add /**/ before the GO statement.
Just replace the current statement with a .Replace("GO", "");