How to generate tests with different names in TestNG? - java

I'm using TestNG to run Selenium based tests in Java. I have a bunch of repeated tests. Generally, they do all the same except of test name and one parameter.
I want to automate generation of it. I was thinking about using factory. Is there a way to generate tests with different name? What would be the best approach to this?
As for now I have something like below and I want to create 10 tests like LinkOfInterestIsActiveAfterClick
#Test(dependsOnGroups="loggedin")
public class SmokeTest extends BrowserStartingStoping{
public void LinkOfInterestIsActiveAfterClick(){
String link = "link_of_interest";
browser.click("*",link);
Assert.assertTrue(browser.isLinkActive(link));
}
}
My XML suite is auto-generated from Java code.
Test names are crucial for logging which link is active, and which one is not.

Have your test class implement org.testng.ITest and override getTestName() to return the name you want.

So I connected Factory with DataProvider and used attributes of contexts.
#DataProvider(name = "DP1")
public Object[][] createData() {
Object[][] retObjArr={
{"Link1","link_to_page"},
{"Link2","link_to_page"},
return retObjArr;
}
#Test (dataProvider = "DP1")
public void isActive(String name, String link){
this.context.setAttribute("name", name);
browser.click(link);
Assert.assertTrue(browser.isLinkActive(link));
}
And in the Listener
public class MyListener extends TestListenerAdapter{
#Override
public void onTestSuccess(ITestResult tr){
log("+",tr);
}
//and similar
private void log(String string, ITestResult tr){
List<ITestContext> k = this.getTestContexts();
String testName = tr.getTestClass().getName();
for (ITestContext i: k)
{
if (i.getAttribute("name") != null)
logger.info(testName+"."+i.getAttribute("name"));
}
}
}

Related

How to extend a class having parameterized #BeforeMethod

I want to extend BaseClass from all test cases class.
For some test cases I want to login with Admin credentials and for some with Customer.
Previously this code was working properly as launchBrowserAndLogin() method was not having any parameters.
Is there any way for extending class with parameter?
Here is my code:
public class BaseClass {
public BaseClass() {
}
#BeforeMethod
public void launchBrowserAndLogin(String userType) {
if(userType.equals("Admin")) {
launchBrowserAndUrl(Constants.ADMIN_URL);
login(Constants.ADMIN_USER_NAME, Constants.ADMIN_USER_PASSWORD);
}
if(userType.equals("Customer")) {
launchBrowserAndUrl(Constants.CUSTOMER_TEST_URL);
login(Constants.CUSTOMER_USER_NAME, Constants.CUSTOMER_USER_PASSWORD);
}
}
#AfterMethod
public void tearDown() {
TestDriver.getDriver().quit();
}
}
public class AssignEditDeleteRoleAccessibilityTest extends BaseClass {
CreateUser newUser = new CreateUser();
RoleAssignmentAccessValidation roleAccessValidation = new RoleAssignmentAccessValidation();
#DataProvider(name = "AssignEditDeleteRoleAccessibilityTest")
public static Object[] roleNames() {
return new Object[] {Constants.AGENCY_ASSISTANT_ROLE_NAME, Constants.OPS_MANAGER_ROLE_NAME};
}
#Test ( priority=1, dataProvider = "AssignEditDeleteRoleAccessibilityTest")
public void assignRoleAccessTest(String roleName) {
newUser.createUserAssignRoleAndLogin(roleName);
boolean isAssignRoleOptionAvailable =roleAccessValidation.assignRoleAccess();
assertEquals(isAssignRoleOptionAvailable, false);
}
}
Now I am getting error:
FAILED CONFIGURATION: #BeforeMethod launchBrowserAndLogin
org.testng.TestNGException:
Can inject only one of <ITestContext, XmlTest, Method, Object[], ITestResult> into a #BeforeMethod annotated launchBrowserAndLogin.
If this were my code, I would split up BaseClass into AdminClass and CustomerClass -- this will both solve the issue, and also help organize the tests a little better, because now you will be aware if the scenario under test is either Customer or Admin view based on which class the test case class extends:
public class CustomerClass {
public CustomerClass() {
}
#BeforeMethod
public void launchBrowserAndLogin() {
launchBrowserAndUrl(Constants.CUSTOMER_TEST_URL);
login(Constants.CUSTOMER_USER_NAME, Constants.CUSTOMER_USER_PASSWORD);
}
#AfterMethod
public void tearDown() {
TestDriver.getDriver().quit();
}
}
And Admin class:
public class AdminClass{
public AdminClass() {
}
#BeforeMethod
public void launchBrowserAndLogin() {
launchBrowserAndUrl(Constants.ADMIN_URL);
login(Constants.ADMIN_USER_NAME, Constants.ADMIN_USER_PASSWORD);
}
#AfterMethod
public void tearDown() {
TestDriver.getDriver().quit();
}
}
Then, your test case class can look like this:
public class AssignEditDeleteRoleAccessibilityTest extends CustomerClass{ // or AdminClass
CreateUser newUser = new CreateUser();
RoleAssignmentAccessValidation roleAccessValidation = new RoleAssignmentAccessValidation();
#DataProvider(name = "AssignEditDeleteRoleAccessibilityTest")
public static Object[] roleNames() {
return new Object[] {Constants.AGENCY_ASSISTANT_ROLE_NAME, Constants.OPS_MANAGER_ROLE_NAME};
}
#Test ( priority=1, dataProvider = "AssignEditDeleteRoleAccessibilityTest")
public void assignRoleAccessTest(String roleName) {
newUser.createUserAssignRoleAndLogin(roleName);
boolean isAssignRoleOptionAvailable =roleAccessValidation.assignRoleAccess();
assertEquals(isAssignRoleOptionAvailable, false);
}
}
This will solve your error, but I recognize this also a matter of opinion & personal design preference -- the concept of a test fixture (which is what BaseClass / AdminClass / CustomerClass each represent) can be implemented in many different ways. I personally believe having a fixture for each "starting" scenario is better in terms of organization and maintainability -- if AdminClass ever becomes more complex, this code will be out of the way from CustomerClass.
Another alternative would be to keep BaseClass, but remove the #BeforeMethod and launchBrowserAndLogin() method. Then, you can write CustomerClass and AdminClass to extend BaseClass (so that the #AfterMethod is inherited). Then, CustomerClass and AdminClass() can implement their own versions of launchBrowserAndLogin(), and the test case class would still look the same as this sample.
There are other approaches to resolve this as well, it's just a matter of personal preference in terms of design and maintainability.
Even if this is not your preferred answer, hopefully this at least gives you an alternative approach for future thought.
What you are trying to do won't be as straightforward to achieve. The Data-Provider feeds the #Test method and the #BeforeMethod method runs before the Test Method, so sending a parameter from the second method to the first one to run won't be possible.
Maybe helping yourself with an ITestListener you can work something out.
For more information on what you can parameterize in your #BeforeMethod, refer to the link your error is telling to look at:
https://testng.org/doc/documentation-main.html#native-dependency-injection
There you have listed the possible things it can receive.
You need to have your BaseClass implement org.testng.IHookable and then have the run() method invoke your launchBrowserAndLogin() method.
Here's how your modified base class will look like:
import java.lang.reflect.Method;
import org.testng.IHookCallBack;
import org.testng.IHookable;
import org.testng.ITestResult;
import org.testng.annotations.AfterMethod;
public class BaseClass implements IHookable {
#Override
public void run(IHookCallBack callBack, ITestResult testResult) {
Object[] parameters = testResult.getParameters();
String userType = parameters[0].toString();
launchBrowserAndLogin(userType);
callBack.runTestMethod(testResult);
}
public void launchBrowserAndLogin(String userType) {
if (userType.equals("Admin")) {
System.err.println("Launching Admin flow");
launchBrowserAndUrl(Constants.ADMIN_URL);
login(Constants.ADMIN_USER_NAME, Constants.ADMIN_USER_PASSWORD);
}
if (userType.equals("Customer")) {
System.err.println("Launching Customer flow");
launchBrowserAndUrl(Constants.CUSTOMER_TEST_URL);
login(Constants.CUSTOMER_USER_NAME, Constants.CUSTOMER_USER_PASSWORD);
}
}
#AfterMethod
public void tearDown(Method method) {
//include your tear down logic here
System.err.println("tearDown() for " + method.getName());
}
public void launchBrowserAndUrl(String url) {
//logic goes here
}
public void login(String username, String password) {
//logic goes here
}
}

Best Way to Initialize Web Driver

I am currently in the process of developing an automation framework and would like to ask a question. What is the best way to initialize the web driver?
Should it be in a Base Test Class that every test class will inherent from and in the BeforeClass, initialize it. Or maybe the web driver should be a singleton object. Or should I use a JUnit Rule. My desirable is that I want to be able to execute the test suite on multiple browsers via a property file. It does not necessarily have to be running on multiple threads, (i.e. Selenium Grid) but I do want the ability to run in sequence. So for example, if in a property file, I have IE and chrome set to true, it will run the test cases for IE, then chrome. So, I would like to know the best way to facilitate this. It will also be data driven, via Excel files and junit parameterized tests.
Thanks
We did something like this with JUnit and Cucumber-JVM. We used a singleton for a WebDriver instance. The specific instance that gets created is based on a system property. To run against multiple browsers, we perform separate runs of the Suite with a different system property for the browser type. We manage that in our build tool.
One advantage of Cucumber-JVM is that it's pretty easy to write hooks that run before or after any test. Without that, you'll want to reset some portion of the WebDriver state before each test.
A while ago, in an unrelated case, I wrote a custom test runner to run tests against multiple database systems by extending ParentRunner and BlockJUnit4ClassRunner. It's easier to just script multiple runs of the same suite in the build tool.
I posted a quick pass at https://github.com/sethkraut/multiwebdriver. I'll also post the code below. It would probably need some polish around preparing and clearing the WebDriver instances, but it should be a good starting point.
This class runs an individual test class after populating a WebDriver field.
public class SingleWebDriverTestRunner extends BlockJUnit4ClassRunner {
private final WebDriver webDriver;
public SingleWebDriverTestRunner(Class<?> klass, WebDriver webDriver) throws InitializationError {
super(klass);
this.webDriver = webDriver;
}
// Test Description methods
#Override
protected String getName() {
return super.getName() + " on " + driverName();
}
private String driverName() {
return webDriver.getClass().getSimpleName();
}
#Override
protected String testName(FrameworkMethod method) {
return super.testName(method) + " on " + driverName();
}
#Override
protected Object createTest() throws Exception {
Object o = super.createTest();
for (Field f: o.getClass().getDeclaredFields()) {
if (f.getType().isAssignableFrom(WebDriver.class)) {
f.setAccessible(true);
f.set(o, webDriver);
}
}
return o;
}
}
And this class iterates over multiple WebDrivers creating instances of the previous class
public class MultiWebDriverTestRunner extends ParentRunner<Runner> {
private List<WebDriver> drivers = new ArrayList<WebDriver>(
Arrays.asList(new FirefoxDriver(), new ChromeDriver())
);
public MultiWebDriverTestRunner(Class<?> klass) throws InitializationError {
super(klass);
}
#Override
protected Description describeChild(Runner child) {
return child.getDescription();
}
private List<Runner> children = null;
#Override
protected List<Runner> getChildren() {
if (children == null) {
children = getChildrenNew();
}
return children;
}
protected List<Runner> getChildrenNew() {
List<Runner> runners = new ArrayList<Runner>();
for (WebDriver driver: drivers) {
try {
Class<?> javaClass = getTestClass().getJavaClass();
runners.add(new SingleWebDriverTestRunner(javaClass, driver));
} catch (InitializationError e) {
e.printStackTrace();
}
}
return runners;
}
#Override
protected void runChild(Runner child, RunNotifier notifier) {
child.run(notifier);
}
}

Android JUnit Testing - Test a method in an activity

I receive a string from a server in the main activity .I have to write a test case for that (whether the string is received in the correct format) How do I write a test case for methods in android ? I am completely new to Testing and any resources are greatly helpful
As you don't want to test the Activity but rather the business logic, you could factor out the receiving of the String to an own class and test this class instead in an isolated way where you don't have any Android specifics around. For example, you could write a POJO class ServerValidator which receives a String and returns a boolean value and you use this validator in your activity and can test it without needing any Android surroundings.
public class ServerValidator {
public static boolean validate(String input) {
return ...; // Insert validation logic
}
}
public class ServerValidatorTest {
#Test
public void testValidateFailure() {
final String faultyString = ...;
Assert.assertFalse(ServerValidator.validate(faultyString));
}
#Test
public void testValidateSuccessful() {
final String correctString = ...;
Assert.assertTrue(ServerValidator.validate(correctString));
}
}

Getting data out of JUnit test class in threaded environment

I am running some JUnit tests programatically with JUnitCore, and I want to get some data out of the test class once it is finished (so #AfterClass). Here is a pseudocode example of the constraints I am working under:
public class A {
public static String testData;
public static void runTest() {
JUnitCore juc = new JUnitCore();
juc.run(B);
// This is where I would like to access testData for this
// particular run
}
public static void setTestData(String s) {
testData = s;
}
}
public class B {
// Some #Test methods and stuff omitted
#AfterClass
public static void done(String s) {
A.setTestData(someData);
}
}
My problem is that different threads might be calling runTest(), so testData might be wrong. How do I work around this? I'm so lost.
If you really need/want to go with this design, you can make testData a java.lang.ThreadLocal<String>. This will solve the multi-threading issue.

Making each test method run in its own instance of a test class with TestNG?

So I thought the following code would run fine in TestNG, although it doesn't:
public class Tests {
int i = 0;
#Test
public void testA() {
Assert.assertEquals(0, i);
++i;
}
#Test
public void testB() {
Assert.assertEquals(0, i);
++i;
}
}
Is there a way to make TestNG fire up a new Tests class for each test method?
The common solution is to use an #BeforeMethod method to setup test state,
#BeforeMethod
public void setup() {
i = 0;
}
By far the most common solution to this issue I have found is to use ThreadLocal’s and just deal with the fact that you only have one instance of each Test Class. This deals with all the questions on how to deal with parallel/threaded tests. This works, but is a bit ugly.
private ThreadLocal<Integer> i = new ThreadLocal<>();
#BeforeMethod
public void setup() {
i.set(0);
}
#Test
public void testA() {
Integer i1 = i.get();
Assert.assertEquals(0, i.get().intValue());
i.set(i1 + 1);
}
#Test
public void testB() {
Integer i1 = i.get();
Assert.assertEquals(0, i.get().intValue());
i.set(i1 + 1);
}
Now back to the root of your question, new instances for each method.
I’ve been researching for a few weeks similar topics, and I have identified this is the number one issue I was personally having with TestNG. It has literally driven me crazy.
If I was to ignore the fact that your tests had a bunch of complexities, you could potentially hack together a work around to meet the requirements you listed.
A TestNG #Factory Factory allows you to create new instances of your test classes.
#Factory
public Object[] factory(){
return new Object[]{new Tests(), new Tests()};
}
I’ve now created two Tests instances, to be ran by testNG
Then the issue is your tests still fail, because it will try to run all test methods on your test classes. In order to hack around this you could implement a IMethodInterceptor, and hack together a solution to enforce that each Tests instance only run one method. Maintain a list of methods, and go through them one at a time.
Here is a brute example I hacked together.
public class TestFactory implements IMethodInterceptor {
private List<String> methodsToRun = new ArrayList<>();
private List<Object> testInstances = new ArrayList<>();
#Factory
public Object[] factory(){
return new Object[]{new Tests(), new Tests()};
}
#Override
public List<IMethodInstance> intercept(List<IMethodInstance> methods, ITestContext context) {
ArrayList<IMethodInstance> tempList = new ArrayList<>();
for(IMethodInstance i: methods){
if(testInstances.contains(i.getInstance())){
continue;
}
String mName = i.getMethod().getConstructorOrMethod().getName();
if(!methodsToRun.contains(mName)){
tempList.add(i);
methodsToRun.add(mName);
testInstances.add(i.getInstance());
}
}
return tempList;
}
}
Then add your listener to the top of your Tests class
#Listeners(TestFactory.class)
You can improve this by dynamically creating new instances of the tests in the factory. Also breaking the listener out into it's own file and numerous other improvements, but you get the gist.
Maybe a crazy solution like the above will work for you or someone else.

Categories

Resources