How to have initialization class failure stop execution of the entire suite

181 views
Skip to first unread message

Shruti

unread,
Jan 24, 2022, 9:39:01 PM1/24/22
to testng-users
We have the following way in which i write our test suites:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name="TP397_Procedure5">
  <test thread-count="5" name="Procedure5">
          <parameter name="TP_ID" value="TP397" />
        <parameter name="deviceName" value="R52N75JFZ" />
        <parameter name="platformVersion" value="10" />
        <parameter name="portNumber" value="4723" />
    <classes>
      <class name="com.tp.tp397_v2.Procedure5.TP397_Procedure5_Initialize"/>
      <class name="com.tp.tp397_v2.Procedure5.TP397_Procedure5_Section01"/>
      <class name="com.tp.tp397_v2.Procedure5.TP397_Procedure5_Section02"/>
      <class name="com.tp.tp397_v2.Procedure5.TP397_Procedure5_TearDown"/>
    </classes>
  </test> <!-- Test -->
</suite> <!-- Suite -->

I want the entire suite to fail, if the "com.tp.tp397_v2.Procedure5.TP397_Procedure5_Initialize" fails. In Initialize class we create patients, associate devices to patients, enable bluetooth on Android device, etc. If any of these steps fail, I want the rest of the classes to not be executed + I want a proper error message to be thrown for failure of suite reason from my executionlistener class and  extent listener class.

I tried dependsOnGroups by creating a group for initialize and having the rest of the section classes depend on the initialize group at class level, but that does not work. Could you please let me know a way this can be achieved.

I want and as much as possible to keep the initialize section in a separate class and not include it in the BaseClass BeforeSuite or BeforeClass. 

Thank you

⇜Krishnan Mahadevan⇝

unread,
Jan 25, 2022, 12:10:14 AM1/25/22
to testng-users
Shruti,
Only a failure in a configuration is capable of failing the rest of the test suite. The same goes for upstream dependencies established either via dependsOnGroups (or) dependsOnMethods.

you could have the methods that are part of 

com.tp.tp397_v2.Procedure5.TP397_Procedure5_Initialize

into a group and the methods that belong to the other classes depend on this group.

There was a deviation of behavior in TestNG 7.4.0 wherein such dependencies would be honored IF and ONLY IF you were running with groups enabled (which looking at your suite xml file, doesn't seem to be the case).

So do the following:

1. Upgrade to TestNG 7.5
2. Define methods that are part of Procedure5_Initialize to be part of some group.
3. Define the methods that are part of rest of the classes to be part of some other group and having dependencies on group created in (2)

You should see this work. If it doesn't work, please share a sample project that can be executed to reproduce the problem.

>>> I want and as much as possible to keep the initialize section in a separate class and not include it in the BaseClass BeforeSuite or BeforeClass. 

I am not sure as to what is your rationale for NOT having the initialisation reside as part of a configuration (@BeforeSuite|@BeforeTest).
You could still have them reside as part of a separate class without inheritance, but you would be confined to running your tests ONLY via a suite xml file (wherein the class that contains the BeforeSuite|BeforeTest) resides. 

You can alternatively also explore TestNG listeners which could house this logic and then it could throw SkipException for e.g.,

Thanks & Regards
Krishnan Mahadevan

"All the desirable things in life are either illegal, expensive, fattening or in love with someone else!"
My Scribblings @ http://wakened-cognition.blogspot.com/
My Technical Scribblings @ https://rationaleemotions.com/


--
You received this message because you are subscribed to the Google Groups "testng-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to testng-users...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/testng-users/87327a64-4a29-4f5d-8cee-5ca093362f9bn%40googlegroups.com.

Shruti

unread,
Jan 25, 2022, 12:34:59 PM1/25/22
to testng-users
Hi Krishnan,

Thank you for your response. Maybe I am just confused or do not know how to achieve this, but as an example I have the pasted our initialization script code below. The amount of code is so large that I am not sure how to add this to the Before section. Could you please provide some insight? If putting the code in Before config method helps, then i don't mind that route.

public class TP397_Procedure1_Initialize extends BaseClass
{
        String TP_ID = "TP-000397";
        CPappProperties cpAppProps = new CPappProperties("TP397", "CPapp.properties");
        long start = System.nanoTime();
        private Logger log = LogManager.getLogger(this.getClass());        
       
        @BeforeClass
        public void setAsInitializeScript(ITestContext context) {
                context.setAttribute("setAsInitializeScript", true);
        }
       
        @Override
        public void beforeEachTestMethod()
        {
                try {
                log.info("beforeEachTest called!");
                resultWriter.setTestProtocolID(TP_ID);
               
                //Record CP Version Number
                log.info("CP Version Number: " + cpApp.Login.getVersionNumber());
               
                //Login to CP, search and select patient James.
                cpApp.Login.Login(cpAppProps.Passcode);
                }catch(Exception e) {e.printStackTrace();}
        }
       
        @FrameworkAnnotation(author = { "Shruti" }, deviceType="ETM1")
        @Test(description="Load cp.configuration.json (BluetoothWhiteList)", groups={"Initialize"} )
        public void setup_LoadCPConfigFile()
        {        
                fail();
                ExtentLogger.logInfo("Add configuration JSON file which contains required device(ETM/ETS) whitelist information to the tablet's Internal Storage/CP folder");
                try {
                //Log4jLogger.startTestCase(new Throwable().getStackTrace()[0].getMethodName());
                //Push CP Config (with WhiteList made from Equipment.properties) into Tablet
               
                TestEquipment Equipment = new TestEquipment("TP397");
                CPConfigManager cpConfigManager = new CPConfigManager();
                cpConfigManager.configFileObject.setBluetoothWhiteList(Equipment.ETXDevices(Equipment.ETM1));
                cpConfigManager.pushConfigFileToTablet();
                }catch(Exception e) {e.printStackTrace();}
        }
       
        @FrameworkAnnotation(author = { " Shruti  " }, deviceType="ETM1")
        @Test(description="Delete all patients", groups={"Initialize"} )
        public void deleteAllPatients()
        {        
                ExtentLogger.logInfo("Delete all existing patients in the CP");
                //Delete all patients in CP App
                cpApp.MyPatients.deleteAllPatientsInCPApp();
        }
       
        @FrameworkAnnotation(author = { " Shruti  " }, deviceType="ETM1")
        @Test(description="create_patient", dataProvider="patient_name", groups={"Initialize"})
        public void setup_createPatient(String patientName) {                
                ExtentLogger.logInfo("Create patient: " + String.format("create_patient %s", patientName));
               
            PatientData patient = new PatientData("TP397", String.format("Patient_%s.properties",patientName));
                               
                //////////////////////
                //Create patient
                /////////////////////
                cpApp.MyPatients.AddPatient();
                cpApp.MyPatients.CreateNewPatient();
                cpApp.Patient.waitForVisibilityOf(cpApp.Patient.Profile.inputPatientName, patient.PatientName);
                cpApp.Patient.Profile.SavePatientInfo(patient.PatientName, patient.ClinicalIndication);
                cpApp.Patient.waitForVisibilityOf(cpApp.Patient.Profile.confirmDialogBox, "confirmDialogbox");

               
                //Evaluation of patient saved successfully dialog box and message
                log.info(cpApp.Patient.Profile.confirmDialogBox.isDisplayed());
                log.info(cpApp.Patient.Profile.isPatientSavedSuccessfully());
               
                //Click on confirm and wait to go back to My patients screen
                cpApp.Patient.Profile.clickOnConfirmButton();
                cpApp.Patient.waitForVisibilityOf(cpApp.Patient.Profile.inputPatientName, patient.PatientName);
                cpApp.Patient.ActionBar.NavigateBack();
                cpApp.Patient.waitForVisibilityOf(cpApp.MyPatients.patientList, "patientList");                
               
        }
       
        @FrameworkAnnotation(author = { " Shruti  " }, deviceType="ETM1")
        @Test(description="Enable_bluetooth")
        public void setup_enableBLE() {
                ExtentLogger.logInfo("Enable bluetooth on the Tablet device");
                cpApp.Bluetooth.enableDeviceBluetooth();
                long finish = System.nanoTime();
                System.out.println("Total time: " + ((finish - start)/1000000));
        }

       
        @DataProvider(name="patient_name")
        public Object[][] createPatient() {
                 return new Object[][] {
                           {"James" },
                           {"Thomas"}
                 };
        }

Shruti

unread,
Jan 25, 2022, 12:40:24 PM1/25/22
to testng-users
Also our baseclass is as follows:

@Listeners({ExecutionListener.class, ExtentListener.class})
public abstract class BaseClass implements ITest{

        protected static ResultWriterCSV resultWriter;
        private static Runtime runTime = Runtime.getRuntime();
        private static Boolean setupComplete = false;

        protected static AppiumDriver<MobileElement> driver;
        private AppiumDriverLocalService service;
       
        public static ClinicianProgrammer cpApp;
        public static TabletIO tablet;
        private static DesiredCapabilities caps;
        protected Logger log = LogManager.getLogger(this.getClass());                
        protected ThreadLocal<String> testName = new ThreadLocal<>();
        private String TestID;
        private LoadPropertiesFile loadPropFile;
        private static File appium_logfile_folder;
       
        @BeforeSuite
        public void timestamp_AppiumLogs_Folder(ITestContext ctx) {
                //If appium_logfile_folder exists from previous run, timestamp it
                appium_logfile_folder = new File(TestDataConstants.APPIUM_LOGFILE_LOCATION + "/" + ctx.getSuite().getXmlSuite().getName());
                String fileName = new SimpleDateFormat("yyyy-MM-dd-HH-mm").format(new Date());
               
                if(appium_logfile_folder.exists()) {
                        appium_logfile_folder.renameTo(new File(appium_logfile_folder + "-" + fileName));
                        appium_logfile_folder.delete();
            }
        }
       
        @BeforeTest
        public void setup(ITestContext ctx) {        
                setupComplete = false;
                resultWriter = ResultWriterCSV.getInstance();
               
                try {
                        runTime.exec("adb shell am force-stop 'Nalu.CP.Android'");
                       
                } catch (IOException e) {
                        e.printStackTrace();
                }
               
                //Create output folder within the outputs/AppiumLogs folder with TP name and procedure name
                if(!appium_logfile_folder.exists()) {
                        appium_logfile_folder.mkdir();
                }
        }
       
        @Parameters({"deviceName","platformVersion", "portNumber"})
        @BeforeClass
        public void initialize( String deviceName, String platformVersion, String portNumber) throws MalformedURLException, WebDriverException{
                try {
                                log.info("BeforeClass from NaluTestSection");
                                //Start Appium Service
                                if(checkIfServiceIsRunning(Integer.parseInt(portNumber)))
                                        stopAppiumService();
                               
                                try {
                                        startAppiumService(portNumber, this.getClass().getSimpleName());
                                } catch (Exception e) {
                                        e.printStackTrace();
                                }
                               
                                //Set Appium driver capabilities
                                loadPropFile = new LoadPropertiesFile(System.getProperty("user.dir")  + "\\framework.properties");
                                loadPropFile.loadPropertiesFile();
                                caps = new DesiredCapabilities();

                                caps.setCapability("platformName", loadPropFile.getProperty("appium.platformName"));
                                caps.setCapability("newCommandTimeout", loadPropFile.getProperty("appium.newCommandTimeout"));
                                caps.setCapability("resetKeyboard", loadPropFile.getProperty("appium.resetKeyboard"));
                                caps.setCapability("autoGrantPermissions", loadPropFile.getProperty("appium.autoGrantPermissions"));
                                caps.setCapability("disableAndroidWatchers", true);
                                caps.setCapability("skipUnlock", true);
                                caps.setCapability(MobileCapabilityType.PLATFORM_VERSION, platformVersion);
                                caps.setCapability(MobileCapabilityType.UDID, deviceName);
                                caps.setCapability(AndroidMobileCapabilityType.APP_PACKAGE, loadPropFile.getProperty("appium.appPackage"));
                                caps.setCapability(MobileCapabilityType.AUTOMATION_NAME, loadPropFile.getProperty("appium.automationName"));
                                caps.setCapability(AndroidMobileCapabilityType.APP_ACTIVITY, loadPropFile.getProperty("appium.appActivity"));        
                                caps.setCapability(MobileCapabilityType.NO_RESET, loadPropFile.getProperty("appium.dontStopAppOnReset"));        
                               
                                log.info("Starting Appium driver...");
                               
                                //Start Appium driver
                               
                                        driver = new AppiumDriver<MobileElement>(new URL("http://127.0.0.1:" + portNumber + "/wd/hub"), caps);
                               
                                log.info("Appium driver started...");
                                cpApp = new ClinicianProgrammer();
                                tablet = new TabletIO();
                } catch (WebDriverException e) {                        
                        try {
                                log.info("Retrying Appium driver start...");
                                driver = new AppiumDriver<MobileElement>(new URL("http://127.0.0.1:" + portNumber + "/wd/hub"), caps);
                        }catch(WebDriverException we) {
                                log.error("Error starting Appium driver..." + we);
                                we.printStackTrace();
                                throw new WebDriverException();
                        }
                } catch (MalformedURLException e) {                
                        log.error("Error starting Appium driver..." + e.getMessage());
                        log.error("Error starting Appium service...: full stack trace follows:", e);
                        e.printStackTrace();
                        throw new MalformedURLException();
                }
        }
       
        public static WebDriver getDriver() {
                return driver;
        }
       
        /*Here alwaysRun is set to true because in normal circumstances BeforeMethod is skipped if BeforeClass fails
          we want to set the TestName to be set even if the BeforeClass fails. Setting alwaysRun to true ensures
          that the testName will always be set. This prevents bug in extentListener which was taking the previous testcase Name
          instead of the current one while logging in Extent Reports
          */
        @BeforeMethod(alwaysRun=true)
        public void setUpTest(Method method, Object[] testData, ITestContext ctx) {        
                log.info("BeforeMethod from NaluTestSection");
                try {
                        Map<String,String> map = new HashMap<String,String>();
                       
                         //Updates names of testcases that use DataProviders
                        if (testData.length > 0) {
                                 //Code used for dataproviders from excel sheets that contain TestID
                                String[] entries = testData[0].toString().split(",");
                                if(entries.length>1) {
                                        for(String entry:entries) {
                                                map.put(entry.split("=")[0].trim(), entry.split("=")[1].trim());
                                        }
                                       
                                        TestID = map.get("TestID");
                                        testName.set(method.getName() + "_" + TestID);
                                        ctx.setAttribute("testName", method.getName() + "_" + TestID);
                                }
                                else {
                                        //Code used for dataproviders that have single entry and no TestID's, viz create_patient
                                        testName.set(method.getName() + "_" + testData[0].toString());
                                    ctx.setAttribute("testName", method.getName() + "_" + testData[0].toString());
                                }
                         } else {
                                 TestID = method.getName();
                                 testName.set(method.getName());
                                 ctx.setAttribute("testName", method.getName());
                         }
       
       
                         log.info("Testname in NaluTestSection is: " + ctx.getAttribute("testName"));
                         
                        //Also check if app is already running. Helpful in conditions where app shutsdown in middle of any testcase though setup was completed successfully
                        if (!setupComplete && driver!=null){
                                log.info("Relaunching app due to setup incomplete or driver==null...");
                                try {
                                        driver.launchApp();
                                        beforeEachTestMethod();
                                }catch(Exception e) {
                                        e.printStackTrace();
                                        throw new Exception();
                                }
                        }
                        setupComplete=true;
                }catch(Exception e) {e.printStackTrace();}
        }
        
On Monday, January 24, 2022 at 9:10:14 PM UTC-8 Krishnan Mahadevan wrote:

⇜Krishnan Mahadevan⇝

unread,
Jan 26, 2022, 11:22:52 PM1/26/22
to testng-users
Shruti,

It's not going to be possible for me to suggest refactoring of code over email without being able to look at it from within an IDE to understand the context and intent.

You mentioned that one of the tests "com.tp.tp397_v2.Procedure5.TP397_Procedure5_Initialize" would be the deciding factor, wherein if there is a failure in it, then downstream test classes need to be skipped.

But it looks like "com.tp.tp397_v2.Procedure5.TP397_Procedure5_Initialize" is a full fledged test class on its own. 

So it's not clear to me as to why your other tests have a dependency on this test as if it were a configuration.

By converting "com.tp.tp397_v2.Procedure5.TP397_Procedure5_Initialize" into a configuration, it would no longer remain a test class and would not be able to evaluate conditions independently.
So converting it into a configuration is NOT maybe going to help you. You would perhaps have to spend time to segregate the configuration parts from it and then house them into a configuration class (This could mean that you may also have to pull out some of the tests into a configuration as well).

The other easier alternative wouldbe try and leverage groups and dependsOnGroups (as I suggested before) wherein all the methods within "com.tp.tp397_v2.Procedure5.TP397_Procedure5_Initialize" would be part of a group say "primary" (don't forget to add alwaysRun=true to your configuration method) and the methods that are part of the other classes would be part of another group say "second" with dependsOnGroups="primary". See if that helps you because this is a lot less invasive change.

You can perhaps first build some dummy tests which model your actual tests, do this as a trial and see if it works the way you want (Don't forget to use TestNG 7.5)

Thanks & Regards
Krishnan Mahadevan

"All the desirable things in life are either illegal, expensive, fattening or in love with someone else!"
My Scribblings @ http://wakened-cognition.blogspot.com/
My Technical Scribblings @ https://rationaleemotions.com/

Reply all
Reply to author
Forward
0 new messages