java-client
java-client copied to clipboard
No such session exception showing up
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)
I don't see any errors in the server log. I believe it is some issue with the client code.
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.
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 !!!