Creating common test data for multiple feature files - karate

My requirement is as follows:
I have a couple of .feature files. I want to create test data that would be common to all of these feature files. Once the test data is created the scenarios will be run from the feature files.
I also want some info back after the test data is created. eg., ids of the data that i have created. So i can use this info to call the api's, add in payloads in my scenarios.
I think we could do this by:
1. Creating a junit java file. I define a static method with #BeforeClass in there and use Karate's runner() to run my create-test-data.feature file (I can use Karate to hit application api to create some data). I define a property in my java class of type Object and set it with the result of Runner.runFeature().
Then I create a separate feature file test-data-details.feature. I define my Java Interop code here. eg.,
def test_data =
"""
var JavaOutput = Java.type('com.mycompany.JavaFile');
var testData = JavaOutput.propertyName;
"""
Now that I have my test data object in my test-data-details.feature file. I call this .feature file (callonce) in the Background section of my feature files that have test scenarios in. So I can retries the test data details like id, name. etc that I can then use in the api request paths and payloads.
I am not sure if the above design is the correct way to go ahead. I tried but getting some issues in my Java file where getClass() below complains that it cannot be used in static method.
#RunWith(Karate.class)
public class AccountRunner {
public static Object job = null;
#BeforeClass
public static void create_job(){
Map<String, Object> result = Runner.runFeature(getClass(), "test-data.feature", null, true);
job = result.get("job");
}
}
Now all of the above can be totally wrong. Need help on how to tackle this scenario in Karate.
Thanks

From your question I understand you have a common test data feature file, which you want to run before all the test and hold that response in a variable that can be used in all of the test features.
You can also achieve this in karate-config.js using karate.callSingle()
In your karate-config.js
config["testdata"] = karate.callSingle("test-data.feature")
Your test-data.feature will be executed once before all the tests and store the response in testdata you can use this variable directly in your feature.

So i have implemented the following design:
I have created two methods one with BeforeClass and other with AfterClass annotation in my TestRunner.java file. In these methods I am able to call the specific data creation and clean-up feature files and pass args as Json object.
#RunWith(Karate.class)
#KarateOptions(tags = {"~#ignore"})
public class AccountRunner {
public static Map<String, Object> result = null;
#BeforeClass
public static void create_job() throws IOException, ParseException {
Class clazz = AccountRunner.class;
URL file_loc = clazz.getResource("create-test-data-1.json");
File file = new File(file_loc.getFile());
JSONParser parser = new JSONParser();
Object obj = parser.parse(new FileReader(file));
JSONObject jsonObject = (JSONObject) obj;
Map<String, Object> args = new HashMap();
args.put("payload", jsonObject);
result = Runner.runFeature(CommonFeatures.class, "create-data.feature", args, true);
}
#AfterClass
public static void delete_investigation() {
Map<String, Object> args = new HashMap();
args.put("payload", result);
Runner.runFeature(CommonFeatures.class, "delete-job.feature", args, true);
}
}
To run these tests via command line using "mvn test" command, i have done following changes in pom.xml.
`<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M3</version>
<configuration>
<includes>
<include>**/*Runner.java</include>
</includes>
</configuration>
</plugin>`
With this solution I able to run my tests in IDE by executing the runner directly or by command line. However, I have not found a way to run all of my tests by following the karate suggested approach where I have a *Test.java file at test suite level and I use default maven configuration with "mvn test". The features fails to run as the .feature file is called before the Runner file is executed which has method to create test data for tests.
Maybe someone can suggest what else, I could do to use Karate approach of running *Test.java file instead of each *Runner.java file.

Related

How to pass random variable in runner class, for html report path?

I'm trying to pass the random variable in cucumber runner class, But as this code is out of the method (Top of the method) script is not allowed to concatenate the date or any random values for the output report path.
You can also configure the report location by using ExtentProperties enum as follows. Leave the plugin configuration empty, and configure the report location in your #BeforeClass method:
plugin = {"com.cucumber.listener.ExtentCucumberFormatter:"}
……
……
#BeforeClass
public static void setup() {
ExtentProperties extentProperties = ExtentProperties.INSTANCE;
extentProperties.setReportPath("output/myreport.html");
}
See documentation here - http://www.vimalselvam.com/cucumber-extent-reporter/
You can pass the information by using -Dcucumber.options in maven command.
clean test -Dtest=stepsAndRunner.Runner -Dcucumber.options="-p com.cucumber.listener.ExtentCucumberFormatter:target/cucumber-reports/report-7/22/2019.html"

Reading parameters from TestNG file

I successfully implemented several tests within TestNG framework, where parameters are being read from xml file.
Here is the example block that is executed as first:
#Parameters({ "country" })
#BeforeSuite(alwaysRun = true)
public void prepareRequest(String country, ITestContext cnt) {
LoginInfoRequestParm loginParms = new LoginInfoRequestParm(country);
Headers reqHeaders = new Headers();
reqHeaders.setHeaders(loginParms);
}
The problem/question is, why does it work only if the ITestContext is specified? Once it is removed, the overall suite is broken and it will never come to the specified method prepareRequest(). I was not able to debug it, because I cannt set breakpoint before the method to be able to see what is going on in TestNG itself.
Thank you for your explanation.
To get out of this situation, try something like this
String myPar = context.getCurrentXmlTest().getParameter("country");
if (myPar == null) {
myPar = "INDIA";
}
now myPar can be used, only thing here is if you run class for debug or any other purpose then we are using INDIA. if we run from testng.xml file then it will take values from that file.

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 launch a certain project using SWTBot

Not every plugin can be tested without project. For example, I want to test CDT-Plug-in, therefore I need to import a C-project. But there is no such point in Run Configuration and when I'm trying to record importing actions via SWT Plug-in Test recorder SWTBot can't replay them afterwards. Google is silent on this topic. How do I do that?
A nice way to do this is using the eclipse recource model
Have a look at the package
org.eclipse.core.resources
Here is a method, that creates a new project in the workspace
private IProject getNewOpenProject(IWorkspace wks, String name)
throws CoreException {
System.out.print("Creating project " + name + "...");
IProjectDescription prj1ProjectDescription = wks
.newProjectDescription(name);
IProject prj = wks.getRoot().getProject(name);
prj.create(prj1ProjectDescription, null);
prj.open(null);
System.out.println(" [OK]");
return prj;
}
This method will import your content into the eclipse project
private void importDirIntoProject(File srcPath, IProject prj,
IOverwriteQuery overwriteQuery) throws InvocationTargetException,
InterruptedException {
ImportOperation op = new ImportOperation(prj.getFullPath(), srcPath,
FileSystemStructureProvider.INSTANCE, overwriteQuery);
op.setCreateContainerStructure(false);
op.run(new NullProgressMonitor());
}
This approach uses native eclipse mechanisms. I think this is better than using the inconvenient way over SWTBot.
It's the responsibility of your test to create the necessary resources in its setup method, and clean them after. It's not something to configure in the Run Configuration, but to code in your test.
You can either use SWTBot to import/create a C project, or use the project APIs suggested by beanie.

Scoping in embedded groovy scripts

In my app, I use Groovy as a scripting language. To make things easier for my customers, I have a global scope where I define helper classes and constants.
Currently, I need to run the script (which builds the global scope) every time a user script is executed:
context = setupGroovy();
runScript( context, "global.groovy" ); // Can I avoid doing this step every time?
runScript( context, "user.groovy" );
Is there a way to setup this global scope once and just tell the embedded script interpreter: "Look here if you can't find a variable"? That way, I could run the global script once.
Note: Security is not an issue here but if you know a way to make sure the user can't modify the global scope, that's an additional plus.
Shamelessly stolen from groovy.codehaus :
The most complete solution for people
who want to embed groovy scripts into
their servers and have them reloaded
on modification is the
GroovyScriptEngine. You initialize the
GroovyScriptEngine with a set of
CLASSPATH like roots that can be URLs
or directory names. You can then
execute any Groovy script within those
roots. The GSE will also track
dependencies between scripts so that
if any dependent script is modified
the whole tree will be recompiled and
reloaded.
Additionally, each time you run a
script you can pass in a Binding that
contains properties that the script
can access. Any properties set in the
script will also be available in that
binding after the script has run. Here
is a simple example:
/my/groovy/script/path/hello.groovy:
output = "Hello, ${input}!"
import groovy.lang.Binding;
import groovy.util.GroovyScriptEngine;
String[] roots = new String[] { "/my/groovy/script/path" };
GroovyScriptEngine gse = new GroovyScriptEngine(roots);
Binding binding = new Binding();
binding.setVariable("input", "world");
gse.run("hello.groovy", binding);
System.out.println(binding.getVariable("output"));
This will print "Hello, world!".
Found: here
Would something like that work for you?
A simple solution is to use the code from groovy.lang.GroovyShell: You can precompile the script like so:
GroovyCodeSource gcs = AccessController.doPrivileged( new PrivilegedAction<GroovyCodeSource>() {
public GroovyCodeSource run() {
return new GroovyCodeSource( scriptCode, fileName, GroovyShell.DEFAULT_CODE_BASE );
}
} );
GroovyClassLoader loader = AccessController.doPrivileged( new PrivilegedAction<GroovyClassLoader>() {
public GroovyClassLoader run() {
return new GroovyClassLoader( parentLoader, CompilerConfiguration.DEFAULT );
}
} );
Class<?> scriptClass = loader.parseClass( gcs, false );
That's was the expensive part. Now use InvokeHelper to bind the compiled code to a context (with global variables) and run it:
Binding context = new javax.script.Binding();
Script script = InvokerHelper.createScript(scriptClass, context);
script.run();