Load properties based on test annotation - java

is there a way to tell a Test with annotations or something like that, to load properties based on a custom annotation and run the tests equally to the number of parameters that test has.
For example:
I want to run test A that has values injected with Spring #value , three times, and for run 1 I want the test to get the values from property file X for run 2 from property file Y and you got it, run 3 from property file Z.
#Value("${person.name}")
private String person.name;
#RunTestWithProperties(properties = {X,Y,Z})
#Test
public void testA() {(System.out.println(person.name); }
On the first run, this test would print the person.name from X
properties file, on the second run the test would print the
person.name from Y and so on.
What would be expected:
testA runs 3 times(each run with different properties) from files X, Y and Z;
I could use data providers or something like that, load properties with system variables but it is not the solution I want.
Technologies I use are Java, TestNG and Spring. Any solution is more than welcomed.
Thank you in advance guys!

You can use parameterized tests. You need to create a method annotated with #Parameterized.Parameters where you can load all you data in a collection (Basically the parameters you need to pass for each run).
Then create a constructor to pass the arguments and this constructor argument will be passed from this collection on each run
e.g.
#RunWith(Parameterized.class)
public class RepeatableTests {
private String name;
public RepeatableTests(String name) {
this.name = name;
}
#Parameterized.Parameters
public static List<String> data() {
return Arrays.asList(new String[]{"Jon","Johny","Rob"});
}
#Test
public void runTest() {
System.out.println("run --> "+ name);
}
}
or if you don't want to use constructor injection you can use #Parameter annotation to bind the value
#RunWith(Parameterized.class)
public class RepeatableTests {
#Parameter
public String name;
#Parameterized.Parameters(name="name")
public static List<String> data() {
return Arrays.asList(new String[]{"Jon","Johny","Rob"});
}
#Test
public void runTest() {
System.out.println("run --> "+ name);
}
}

Related

Running a #BeforeMethod testNG annotation when the method *contains* a group

If I have a beforeMethod with a group, and I run a different group, but within that group there exists a test that has both the group that I'm running as well as the group with the beforeMethod, I want that test to run its beforemethod. So for example:
#BeforeMethod(groups = "a")
public void setupForGroupA() {
...
}
#Test(groups = {"supplemental", "a"})
public void test() {
...
}
when I run testNG with groups=supplemental, I still want the beforeMethod to run before test, but because the group is supplemental instead of 'a', it won't.
This seems like such an obvious feature to me that I feel like I must be using the groups incorrectly, so I would also like to explain my workflow as well, in case that's where my issue is.
I'm using groups to define different layers of tests, as well as whether they need their own account to be created or if they need to use a proxy to access their data, etc. I'll have groups of smoke, supplemental and regression as well as groups of uniqueAccount, proxy, etc. I don't need specific setup for the first groupings, but those are the groups I pass in to run in maven. I require specific setups for the latter groups, but I never want to run just the tests that require a proxy, or require a unique account.
Groups configuration is not evaluated at runtime. test method won't activate setupForGroupA method.
The feature is used to find methods you want to run.
According to the following example:
#BeforeMethod(groups = "a")
public void setupForGroupA() {
...
}
#Test(groups = {"supplemental", "a"})
public void test() {
...
}
#Test(groups = {"supplemental"})
public void test2() {
...
}
If you run this class with group "a" it will run setupForGroupA and test methods because they are marked with the group "a".
If you run this class with group "supplemental" it will run test and test2 methods because they are marked with the group "supplemental".
It looks you have different behavior for some methods, so a good approach is to separate methods in different classes and select tests by class instead of select tests by groups.
public class A {
#BeforeMethod
public void setupForGroupA() {
...
}
#Test
public void test() {
...
}
}
and
public class Supplemental {
#Test
public void test2() {
...
}
}
Running class A will run setupForGroupA and test only.
Running class Supplemental will run test2 only.
Running both classes will run everything.
In case you want to run both classes and filter by something else, you can implement your own logic with a method interceptor:
#MyCustomAnnotation(tags = "a", "supplemental")
public class A {
...
}
#MyCustomAnnotation(tags = "supplemental")
public class Supplemental {
...
}
public class MyInterceptor implements IMethodInterceptor {
public List<IMethodInstance> intercept(List<IMethodInstance> methods, ITestContext context) {
// for each method, if its class contains the expected tag, then add it to the list
// expect tag can be passed by a system property or in a parameter from the suite file (available from ITestContext)
}
}
If I get it correct, you want to run your before method every time. In this case, you could set alwaysRun=true for your before method like this-
#BeforeMethod(alwaysRun = true, groups = "a")
public void setupForGroupA() {
...
}
This one of the solutions you want.

blend parameterized and programmatic unit tests

With JUnit you can use #RunWith(Parameterized.class) to provide a set of parameters to pass to the test constructor and then run tests with each object.
I'm trying to move as much test logic as possible into data, but there are some tests that won't easily be converted into data-driven tests. Is there a way to use JUnit's Parameterized runner to run some tests with parameters, and then also add non-data-driven tests that aren't run repeatedly for each test object construction?
My workaround for this was to create a single class and place the programmatic and data-driven tests in two separate sub-classes. A sub-class must be static for JUnit to run its tests. Here's a skeleton:
#RunWith(Enclosed.class) // needed for working well with Ant
public class MyClassTests {
public static class Programmatic {
#Test
public void myTest(){
// test something here
}
}
#RunWith(Parameterized.class)
public static class DataDriven {
#Parameters
public static Collection<Object[]> getParams() {
return Collections.emptyList();
}
private String data;
public DataDriven(String testName, String data){
this.data = data;
}
#Test
public void test() throws AnalyzeExceptionEN{
// test data string here
}
}
}
one way is to use Junit's Enclosed runner. it's very verbose but also pretty powerful. it allows you to combine multiple different runners in one file.
other option is to use custom junit runner. for sure zohhak supports tests with parameters and without. small extract:
#RunWith(ZohhakRunner.class)
public class CoercingTest {
#TestWith("ONE_OF_ENUM_VALUES")
public void should_coerce_enum(SampleEnum param) {
assertThat(param).isEqualTo(SampleEnum.ONE_OF_ENUM_VALUES);
}
#Test
public void should_run_standard_junit_test() {
//this will also work
}
}
if it's not enough for you, for sure you can find other runners that support both kind of tests.

Pass parameters to JUnit test from the TestSuite class

I want to create 2 JUnit TestSuites. They both utilize the same test classes, but they should each use different parameters. For example, in test suite A, I want my data to be collected from file A and to be written to database A. In test suite B, I want my data to be collected from file B and to be written to databaseB.
The reason I use testSuites for this is because:
I can put all the specific parameters in the testsuite classes
I can reuse the testclasses
I can choose which testsuite to run. I do not want all tests to always run with all possible paramaters!
The problem is I cannot really pass the parameters. I understand the way the Parameterized class works with JUnit, but it does not allow point 3 in the list above. If I use the code below it will run my test class with both databse connections, which is not what I want to achieve.
#RunWith(value = Parameterized.class)
public class TestCheckData
{
private File file;
private DatabaseSource databaseSource;
public TestCheckData(File file, DatabaseSource databaseSource)
{
this.file = file;
this.databaseSource = databaseSource;
}
#Parameters
public static Iterable<Object[]> data1()
{
return Arrays.asList(new Object[][]
{
{ TestSuiteA.DATA_FILE_A, TestSuite1.DATABASE_A },
{ TestSuiteB.DATA_FILE_B, TestSuite1.DATABASE_B }
});
}
I already find some way of passing configurations in a spring context in this question, but I'm not using any special framework.
Well, this would be a little unconventional, but you could add a different Test class to the beginning of each suite run that would set the parameters you want to use for that test. So you'd have classes like:
public abstract class StaticParameters {
public static File dataFileToUse = null;
public static DatabaseSource databaseToUse = null;
}
public class Suite1Params extends StaticParameters {
#BeforeClass
public static void setParams() {
dataFileToUse = DATA_FILE_A;
databaseToUse = DATABASE_A;
}
}
public class Suite2Params extends StaticParameters {
#BeforeClass
public static void setParams() {
dataFileToUse = DATA_FILE_B;
databaseToUse = DATABASE_B;
}
}
Then you'd just make Suite1Params or Suite2Params the first in your suite list. You might have to add a fake #Test entry to the params classes, I'm not sure if the Suite runner requires that.
You could modify the tests so that they get the parameters from a config file. This way you would always only have 1 Suite.
The path of the config file can be looked up via a System property.
Then on the invocation of the test suite, you could pass in a different config file by changing the property using the -D option on the JVM.
So for example if you named the proprerty env.properties then your command would be:
%java -Denv.properties=prod.config runMyTests
or
%java -Denv.properties=dev.config runMyTests
etc

TestNG - sharing state between test classes

I have a testng suite of test classes that I am running via a testng.xml file. This works fine. All tests are run serially, so no parallel-execution hurdles.
My goal now is to take state that was generated by the test methods in one test class (e.g., a customerId primary key value generated by a database insert) and pass it along to another test class, so that the test methods in that second class can take further action based on that shared state (e.g., look up the just-inserted customer using the customerId value from the first class).
It's easy to share state between test methods in a single class, of course, via class member variables, but I don't see how to share it across test classes.
Really Testng-y way to do this is to set ITestContext attribute with desired value and then get it from other test class.
Set value:
#Test
public void setvaluetest(ITestContext context) {
String customerId = "GDFg34fDF";
context.setAttribute("customerId", customerId);
}
Get value:
#Test
public void getvaluetest(ITestContext context) {
String id = context.getAttribute("customerId");
}
Use a factory to create a repository. The repository stores "interesting test state" properties.
TestRepository tr = TestRepositoryFactory.getInstance();
...
tr.store("test13.PKID", pkid.toString());
and then in your subsequent code, repeat the call to the factory and then get the value:
String spkid = tr.get("test13.PKID");
Another way is to use means of object oriented programming.
Common structure of tests for example:
TestBase.java (parent class for all other test classes, has methods like #BeforeTest, #AfterSuite, etc.)
RegistrationTests.java (extends TestBase, )
ShoppingTests.java (extends TestBase)
and WhateverElseTests.java (extends TestBase)
So TestBase has all shared data as static fields e.g. Customer object:
public class TestBase {
protected static final BrowserManager bw = new BrowserManager();
protected static Customer customer;
#BeforeSuite
public void initBrowser() {
bw.init();
}
#AfterSuite
public void terminateBrowser() {
bw.terminate();
}
}
And access to customer in tests, e.g. ShoppingTests.java:
public class ShoppingTests extends TestBase {
#Test
public void doSomethingTest() {
bw.navigateTo().shoppingPage();
bw.shoppingPreparationHelper().checkDisplayedName(customer.name);
...
N.B.: tests that share objects should go in a strict sequence (first - test that init object, then - test that uses object's data), so use #Test(dependsOnMethods = "someMethodTest"). Otherwise you are in risk of NullPointerException for customer.
P.S.: Object-oriented way has great advantage over ITestContext because you can pass any object(s) from test to test (also between classes), not just string attribute.
As we know from TestNG JavaDoc, ITestContext defines a test context which contains all the information for a given test run. An instance of this context is passed to the test listeners so they can query information about their environment.
So we can share generated data from one class in this test with another class in this test.
Producer.java
public List<String> groupIds = ...;
#AfterClass(alwaysRun = true)
public void reserveGroupIds(ITestContext ctx) {
ctx.setAttribute(GROUPS_ATTR, groupIds);
}
Consumer.java
public List<String> groupIds;
#BeforeClass(alwaysRun = true)
public void fetchGroupIds(ITestContext ctx) {
groupIds = (List<String>) ctx.getAttribute(Producer.GROUPS_ATTR);
}
mySuite.xml
...
<test>
<classes>
<class name= "Producer"/>
<class name= "Consumer"/>
</classes>
</test>
...

How run junit with parametrized constructor

I have to run junit test from command line and one of the guy in the team created junit classes like below:
public Test extends TestCore
{
String some;
public Test(String some)
{
this.some = some;
}
//some test here
}
this work from the eclipse but doesn't from command line.
The result of execution this kind of file gave me error like below:
Test class should have exactly one public zero-argument constructor.
Anyone could help me?
Cheers Jaroslaw.
Eclipse uses a different testrunner. Maybe the parameterized constructors are caused by TestCore being a parameterized test, e.g. like this:
#RunWith(Parameterized.class)
public class TestCore {
String someThatWillBeHidden;
public TestCore(String some) {
this.someThatWillBeHidden = some;
}
#Parameters
public static List<Object[]> data() {
Object[][] data = new Object[][] { {"Hello"}, {" "}, {"world"}};
return Arrays.asList(data);
}
//some test here
}
So which version of junit are you using?

Categories

Resources