Hacktoberfest 2026:维护者为十月标记出来的 issue,仍然开放、适合新手。 浏览 Hacktoberfest issue

No such session exception showing up

未关闭
#1,628 3 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

维护者通常 5 天内回复

还没有人认领这个 Issue。

评估

难度
4/5
预计耗时
3-5 天
新手友好度
25/100
Issue 类型
缺陷
描述清晰度
需要澄清
活跃度
冷清
技术栈
android, java

调研方向

首先比较运行完整测试套件时 BaseClass 的生命周期与单独测试时的生命周期,然后跟踪 SetupData.setParameters、AreaParameters 和 DataProviders.stimulationTestData。使用提供的 Android 和 Appium 版本重现故障,并检查 driver 何时退出或被替换。当数据驱动测试在完整测试套件中完成且没有 null-session exception 时,即表示完成。

由索引模型根据 Issue 内容生成。

描述

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)

主要语言
Java
星标
1.3k
派生
755
平均合并
5 天 14 小时
30 天内合并 PR
8

环境准备

  • 没有 Dockerfile 或 Docker Compose 文件
  • 有 Pull Request 模板
  • 没有贡献指南

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

appium/java-client 的其他 Issue

查看 appium/java-client 的全部 Issue

相似的 Issue

更多 Java Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。