java-client icon indicating copy to clipboard operation
java-client copied to clipboard

No such session exception showing up

Open skhandekar1 opened this issue 3 years ago • 3 comments

The problem

I am trying to run a test class in a suite. The class that is giving the error is running a data driven test. The beforeMethod in the test runs fine, but for some reason the test fails as soon as it enters the @Test and tries setting a parameter in my Android Mobile test. This problem is consistent and only happens when i run the entire suite of classes. If I run the above mentioned class individually, it runs fine.

Environment

  • Appium version (or git revision) that exhibits the issue: 6.1.0
  • Desktop OS/version used to run Appium: Windows10
  • Node.js version (unless using Appium.app|exe): v12.18.2
  • Npm or Yarn package manager: 6.14.5
  • Mobile platform/version under test: Android 10.0
  • Real device or emulator/simulator: Real Device
  • Appium CLI or Appium.app|exe: Appium cli

Details

Error is as follows:

[ERROR] 2022-01-08 09:54:28 framework.Listeners.ExecutionListener 160 onTestFailure - Error type : 
**org.openqa.selenium.NoSuchSessionException: Session ID is null. Using WebDriver after calling quit()?**
Build info: version: '3.141.59', revision: 'e82be7d358', time: '2018-11-14T08:17:03'
System info: host: 'N-PF2JP03M', ip: '172.22.64.1', os.name: 'Windows 10', os.arch: 'amd64', os.version: '10.0', java.version: '1.8.0_281'
Driver info: driver.version: AppiumDriver
	at org.openqa.selenium.remote.HttpCommandExecutor.execute(HttpCommandExecutor.java:125) ~[selenium-remote-driver-3.141.59.jar:?]
	at io.appium.java_client.remote.AppiumCommandExecutor.execute(AppiumCommandExecutor.java:231) ~[java-client-6.1.0.jar:?]
	at org.openqa.selenium.remote.RemoteWebDriver.execute(RemoteWebDriver.java:552) ~[selenium-remote-driver-3.141.59.jar:?]
	at io.appium.java_client.DefaultGenericMobileDriver.execute(DefaultGenericMobileDriver.java:46) ~[java-client-6.1.0.jar:?]
	at io.appium.java_client.AppiumDriver.execute(AppiumDriver.java:1) ~[java-client-6.1.0.jar:?]
	at io.appium.java_client.HasSessionDetails.getSessionDetails(HasSessionDetails.java:39) ~[java-client-6.1.0.jar:?]
	at io.appium.java_client.HasSessionDetails.getSessionDetail(HasSessionDetails.java:56) ~[java-client-6.1.0.jar:?]
	at io.appium.java_client.HasSessionDetails.isBrowser(HasSessionDetails.java:86) ~[java-client-6.1.0.jar:?]
	at io.appium.java_client.AppiumDriver.isBrowser(AppiumDriver.java:271) ~[java-client-6.1.0.jar:?]
	at io.appium.java_client.pagefactory.utils.WebDriverUnpackUtility.lambda$0(WebDriverUnpackUtility.java:87) ~[java-client-6.1.0.jar:?]
	at java.util.Optional.map(Unknown Source) ~[?:1.8.0_281]
	at io.appium.java_client.pagefactory.utils.WebDriverUnpackUtility.getCurrentContentType(WebDriverUnpackUtility.java:83) ~[java-client-6.1.0.jar:?]
	at io.appium.java_client.pagefactory.AppiumElementLocator.getBy(AppiumElementLocator.java:90) ~[java-client-6.1.0.jar:?]
	at io.appium.java_client.pagefactory.AppiumElementLocator.findElement(AppiumElementLocator.java:117) ~[java-client-6.1.0.jar:?]
	at io.appium.java_client.pagefactory.interceptors.InterceptorOfASingleElement.intercept(InterceptorOfASingleElement.java:60) ~[java-client-6.1.0.jar:?]
	at io.appium.java_client.android.AndroidElement$$EnhancerByCGLIB$$b598166c.clear(<generated>) ~[java-client-6.1.0.jar:?]
	at framework.Screens.Patient.Sessions.CommonComponents.Area.AmplitudeControls.setAmplitude(AmplitudeControls.java:42) ~[test-classes/:?]
	at framework.commonUtilities.DataProvider.SetupData.setParameters(SetupData.java:32) ~[test-classes/:?]
	at com.tp.tp397_v2.Procedure4.TP397_Procedure4_Section08.execute_SNSConfiguredLeadProgram(TP397_Procedure4_Section08.java:160) ~[test-classes/:?]
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_281]
	at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) ~[?:1.8.0_281]
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) ~[?:1.8.0_281]
	at java.lang.reflect.Method.invoke(Unknown Source) ~[?:1.8.0_281]

I tried printing out the session ID i get from my driver at various places to find where its getting set to null, but i am not seeing it set to null anywhere:

	@BeforeMethod
	public void BeforeMethod(Method method, Object[] testData){
		//Set testName for each testcase
		log.info("\n******************************************************\n"
				+ "BeforeMethod called from TP397_Procedure4_Section08\n******************************************************\n");

	
		for(String str : testData[0].toString().split(","))
		{
			if(str.contains("TestID=")) {
				testName.set(method.getName() + "_" + str.split("=")[1]);
				break;
			}
		}

		//If CP is on login screen in case of a test failure OR for the very first execution
		//Login to CP, search and select patient Rosa.
		if(cpApp.Login.isVisible(cpApp.Login.clinicLogo, "txtClinicName")) {
			cpApp.Login.Login(cpAppProps.Passcode);
			cpApp.MyPatients.Search(patient.PatientName);
			cpApp.MyPatients.EnterFirstPatient();

			//From Sessions tab, create an OR session
			cpApp.Patient.TapSessions();
		}
		//Successive executions: CP is on OR Session screen.
		else {
			try{
				//For valid stimulation scenarios
				if(ORScreen.isStimulationActive()) { }
			}catch(Exception e) { }

			ORScreen.TapDone();
		}
		cpApp.Patient.waitForVisibilityOf(cpApp.Patient.sessionsTab, "sessionsTab");
		cpApp.Patient.Sessions.AddSession();
		cpApp.Patient.waitForVisibilityOf(cpApp.Patient.Sessions.ORSessionButton, "btnORSession");
		cpApp.Patient.Sessions.ChooseORSession();
		ORScreen.isDisplayed(patient.ClinicalIndication);
System.out.println("Session id in execute_SNSConfiguredLeadProgram @BeforeMethod is: " + driver.getSessionId());
	}

	@SuppressWarnings("unlikely-arg-type")
	@FrameworkAnnotation(author = { "RShah" }, deviceType="ETM1")
	@Test (description="Data-driven Test for SNS Configured Lead Program", dataProvider="stimulationTestData", dataProviderClass=DataProviders.class)
	public void execute_SNSConfiguredLeadProgram(Map<String, String> data) {	
		//Test ID which are covered in this test
		//TP397-025
		//TP397-026
		//TP397-027
		//TP397-028
		//TP397-029
		//TP397-030
		//TP397-031
		//TP397-032
		//TP397-033
System.out.println("Session id in execute_SNSConfiguredLeadProgram @Test (1) is: " + driver.getSessionId());
			log.info("The parameters used for this test are: \n" + data);
			ExtentLogger.logInfo("The parameters used for this test are: <br>" + data);
System.out.println("Session id in execute_SNSConfiguredLeadProgram @Test (2) is: " + driver.getSessionId());			
			ResultWriterParams.testCaseId = data.get("TestID");
			ResultWriterParams.acceptanceCriteria = String.format("Test CP Log with following parameters %s", programParameters);
			NTestCase testID = new NTestCase(ResultWriterParams.testCaseId, TP_ID);
System.out.println("Session id in execute_SNSConfiguredLeadProgram @Test (3) is: " + driver.getSessionId());
			if(data.get("TestType").toLowerCase().matches("configuredlead_valid")) {
				//Set OR Session parameters according to datasheet
				ExtentLogger.logInfo("Set OR Session parameters according to datasheet");
System.out.println("Session id in execute_SNSConfiguredLeadProgram @Test (4) is: " + driver.getSessionId());
				SetupData.setParameters(DataProviderType.OR_SESSION_DATA, data);

Point it fails at is SetupData.setParameters(DataProviderType.OR_SESSION_DATA, data);

The relevant portion where failure occurs in my SetupData class is as follows:


public class SetupData {
	private static AreaParametersComponent AreaParameters = new AreaParametersComponent();
	private static ORSessionScreen ORScreen= new ORSessionScreen();
	
	
	//From sessionType figure out the workbook to look at for test data
	static Map<String, String> WorkBookName = new HashMap<String, String>() {{
			  put("TP397_ORSession", TestDataConstants.OR_STIMULATION_TESTDATA_FILE);
			  put("TP368_programmingSession", TestDataConstants.STIMULATION_TEST_DATA_FILE);		
	}};
	
	//Setup data 
	public static void setParameters(DataProviderType dataType, Map<String, String> data) {
		switch(dataType) {
			case PATIENT_DATA:
				//Do patient data related setup
			case OR_SESSION_DATA:
				//Method to set OR Session parameters according to data from Stimulation dataSheets
				//Data is received as a HashMap argument
				AreaParameters.Amplitude.setAmplitude(data.get(TestData.ORSessionData.OR_Amplitude));
				AreaParameters.PulseWidth.setPulseWidth(data.get(TestData.ORSessionData.OR_PulseWidth));
				AreaParameters.Rate.setRate(data.get(TestData.ORSessionData.OR_ETX_Rate));
				setElectrodes(data);
							
			case PROGRAMMING_SESSION_DATA:
				//Do programming session related setup
		}
	}

My Dataproviders class is as follows:

public class DataProviders {
	private Logger log = LogManager.getLogger(this.getClass());
	private static String worksheet;
	private static String testType_String;
	private static String Tp_Id;
	
	
	public static void setTestParameters(String workSheetName, String testTypeString, String TP_ID) {
		worksheet = workSheetName;
		testType_String = testTypeString;
		Tp_Id = TP_ID;
	}
	
	/*
	 * DataProvider which converts the excel data to a hashmap and returns hashmap as a two-dimensional object array
	 * that @Test tests can use to iterate over
	 */
	@DataProvider
	public static Object[][] stimulationTestData(Method method) {
		List<String> testIDs;
		int iTestCaseRow;
		
		//Create new results arraylist
		List<Map<String, String>> results = new ArrayList<>();
		
		try {
			ExcelUtils.setExcelFile(SetupData.WorkBookName.get(Tp_Id), worksheet);
			
		    // Fetching the TestCase ID's related to particular TestType
		 	testIDs = ExcelUtils.getAllTestIDListForTestType(testType_String, 1, 2);
		 	System.out.println(testIDs);
		 				
		    //Only get rows that belong to the testID's
		 	for(String testID:testIDs) {
			 	// Getting the Test Case row which contains the testID
		 		try {
					iTestCaseRow = ExcelUtils.getRowContains(testID,2);

					//Get keys from header row (Row 1) and 
					List<String> keys = ExcelUtils.getValuesInSpecificRow(1);			
			 		List<String> values = ExcelUtils.getValuesInSpecificRow(iTestCaseRow);
			 		
			 		results.add(ExcelUtils.transform(keys, values));
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} 	 				 		
	 		}
		} catch (Exception e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
	 	
	 	return ExcelUtils.asTwoDimensionalArray(results);	
	}	
}

My Base class is as follows (Renamed for security reasons):

@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 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'.csv'").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 '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 BaseClass");
				//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);
System.out.println("Session id in BaseClass @BeforeClass is: " + driver.getSessionId());
				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 BaseClass");
		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());
					}
			    testName.set(method.getName() + "_" + map.get("TestID"));
			    ctx.setAttribute("testName", method.getName() + "_" + map.get("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 {
				 testName.set(method.getName());
				 ctx.setAttribute("testName", method.getName());
			 }
	
	
			 log.info("Testname in BaseClass 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();}
	}
	
	@Override
	public String getTestName() {
		return testName.get();
	}
	
	public void beforeEachTestMethod()
	{
		//This should be overridden.
	}
	
	@AfterMethod
	public void afterMethod(ITestResult result) {
		/*
		 	App force shutsdown under two conditions:
				1) If setup did not complete successfully in the @Before method
				2) If setupComplete = true, but test fails, check the custom restart flag and then reset the setupComplete flag shutdown app
						By default restart flag is set to true
		*/


		if(Globals.restartAppFlag) {		
			log.info("\n**********************************************\n"
					+ "AfterMethod called from BaseClass\n**********************************************\n");
System.out.println("Session id in BaseClass @AfterMethod is: " + driver.getSessionId());
			if ((!setupComplete) || ((setupComplete==true && Globals.testFailedStatus==true))){
				log.info("CP App will be shutdown due to above failure");
				//driver.quit();
				setupComplete = false;
			}
		}
		
	}
	
	@AfterClass(alwaysRun=true)
	public void afterClass() {		
		log.info("\n*******************************\n"
				+ "After class cleanup executed from NTestSection\n*******************************\n");
		//Set setupComplete to false so that @BeforeMethod runs before each class
System.out.println("Session id in NTestSection @AfterClass is: " + driver.getSessionId());
		driver.quit();
System.out.println("Session id in NTestSection @AfterClass is: " + driver.getSessionId());
		setupComplete = false;
		stopAppiumService();		
	}
	
	@AfterTest
	public void cleanup() {
//		log.info("\n*******************************\n"
//				+ "After test cleanup executed\n*******************************\n");	
	}	
	
	public void startAppiumService(String portNumber, String className) throws Exception {
		log.info("Starting Appium service. /n Logs will be stored at: " + appium_logfile_folder + "/appium_"+ className + ".log");
		File appium_logfile = new File(appium_logfile_folder + "\\appium_"+ className + ".log");
		
		try {
		AppiumServiceBuilder builder = new AppiumServiceBuilder();
		builder.withIPAddress("127.0.0.1");
		builder.usingPort(Integer.parseInt(portNumber));
		builder.withLogFile(appium_logfile);
		//builder.withArgument(GeneralServerFlag.LOG_LEVEL, LogLevel.ERR.toString());
		service = AppiumDriverLocalService.buildService(builder);
		service.start();
		service.clearOutPutStreams();
		log.info("Service has been started with port number " + portNumber);
		}catch(Exception e) {
			log.error("Error starting Appium service...");
			try {
				log.info("Retrying Appium service start");
				service.start();		
				service.clearOutPutStreams();
			}catch(Exception ae) {
				log.error("Error starting Appium service...: full stack trace follows:", ae);
				throw new Exception();
			}
		}
	}
	
	public void stopAppiumService() {

		Runtime runtime = Runtime.getRuntime();
		try {
			runtime.exec("taskkill /F /IM node.exe");
			runtime.exec("taskkill /F /IM cmd.exe");
			log.info("Appium service stopped...");
		} catch (IOException e) {
			log.error("Could not stop Appium service...");
			e.printStackTrace();
		}
	}
	
	public static boolean checkIfServiceIsRunning(int port) {
		boolean isServerRunning = false;
		ServerSocket serverSocket;
		try {
			serverSocket = new ServerSocket(port);
			serverSocket.close();
		}catch(IOException e) {
			//If control comes here, then it means that the port is in use
			isServerRunning = true;
		}finally {
			serverSocket = null;
		}
		if(isServerRunning)
			System.out.println("Appium service is already running on port: " + port);
		return isServerRunning;
	}
}

Link to Appium logs

(https://gist.github.com/skhandekar1/e4d300b1ae9619009191c0398e948b4d)

skhandekar1 avatar Jan 08 '22 18:01 skhandekar1

I don't see any errors in the server log. I believe it is some issue with the client code.

mykola-mokhnach avatar Jan 08 '22 22:01 mykola-mokhnach

That's too much code for us to adequately review. My advice would be to start stripping away everything except a bare skeleton from that code until you have a minimal reproducible scenario. It will probably make obvious what the issue is in your code.

jlipps avatar Jan 11 '22 00:01 jlipps

I was getting this issue but seems issues with dependency , as appium takes selenium dependencies transitively I removed a selenium java pom dependency as appium client had it, worked after that. Not sure if this helps but my 2 cents !!!

prabhurohith avatar Mar 22 '22 12:03 prabhurohith