Garbage Collected on POM framework

Hello everyone. As a newbie in Appium, I recently follow a Course video and when I tried the POM framework, it throw this exception:

java.lang.IllegalStateException: The element By.id: nameText is not locatable anymore because its context has been garbage collected

Here’s my Page Object class:

public class LoginPage extends AndroidGestures {
	
	AndroidDriver driver;
	
	public LoginPage(AndroidDriver driver) {
		super(driver);
		this.driver = driver;
		PageFactory.initElements(new AppiumFieldDecorator(driver), this);
	}
	
	@AndroidFindBy(id="com.androidsample.generalstore:id/nameField")
	private WebElement nameText;
	
	@AndroidFindBy(id="com.androidsample.generalstore:id/radioMale")
	private WebElement genderMale;
	
	@AndroidFindBy(id="com.androidsample.generalstore:id/radioFemale")
	private WebElement genderFemale;
	
	@AndroidFindBy(id="com.androidsample.generalstore:id/btnLetsShop")
	private WebElement loginButton;
	
	@AndroidFindBy(id="android:id/text1")
	private WebElement country;
	
	public void setName(String name) {
		nameText.sendKeys(name);
		driver.hideKeyboard();
	}
	
	public void setGender(String gender) {
		if(gender.toLowerCase().equals("female")) {
			genderFemale.click();
		} else if (gender.toLowerCase().equals("male")) {
			genderMale.click();
		}
	}
	
	public void selectCountry(String cntr) {
		country.click();
		scrollDownToTextAndClick(cntr);
	}
	
	public void login() {
		loginButton.click();
	}
}

Here’s my Test class:

public class BasicStore extends BaseClass {
	
	LoginPage lgp = new LoginPage(driver);
	
	@Test (priority = 1, description = "Enter the general store")
	public void POM001() {
		
		threadSleep(10);
		
		WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
		wait.until(ExpectedConditions.visibilityOfElementLocated(AppiumBy.id("com.androidsample.generalstore:id/nameField")));
		
		// filling the form
		lgp.setName("Ayumi");
		lgp.setGender("female");
		lgp.selectCountry("Bahamas");
		lgp.login();
		
		threadSleep(3);
		
		// assert that we enter the correct page
		Boolean page = driver.findElement(AppiumBy.id("com.androidsample.generalstore:id/appbar_btn_cart")).isDisplayed();
		Assert.assertTrue(page);
	}
}

Several things I have tried:

  • Using thread.Sleep() to wait for the splash screen to finish
  • Using WebDriverWait until element is visible
  • Downgrading my JDK from 17 to 11
  • Re-installing the apps (!) from the phone emulator
  • Increasing VM Heap size

None of the above works as I still encountering the same issue. Please help on this, and if anymore details needed to be added, feel free to ask. Many thanks in advance!

you have to inspect this ID correctly this message means the ID does not inspect, you have to use uiautomator2 to catch it. and before you run your script try to copy this ID to check if it is inspected or not before you loop it inside the method.

Please provide the full stracktrace.

In general I am not quite sure about this line

LoginPage lgp = new LoginPage(driver);

I would expect the page object to be either initialized in the test/suite setup section or the test itself after the driver object is properly initialized. It might be it is given a wrong/obsolete instance

I would expect the page object to be either initialized in the test/suite setup section or the test itself after the driver object is properly initialized. It might be it is given a wrong/obsolete instance

I moved it inside the Test section and now it worked :upside_down_face:. but I notice, this is not very re-usable for all test inside the class, to which I have to keep calling them for every test I have. may I know if anyone have better approach on this?

last but not least, thanks guys for helping on this

Hello, Mykola. I have exactly the same problem, but nothing helps. That is, without changing anything in the code, my tests stopped passing. Out of 10 tests, one, the very last one, passes, the rest go to ‘garbage collected’.

I updated testng, appium java client, and selenium support to the latest versions (after that I started struggling with ‘garbage’ problem), then rolled back, and then tried to update one at a time, it didn’t help.

Now the pom file doesn’t list the latest versions, but it still doesn’t work.
When I run each of these tests individually, each one passes successfully.

I will give examples of code for one of the tests (the rest look about the same), a page object class, a pom file, and an XML file. And also the error that is displayed.

Could you please share your thoughts, any assumptions will be much appreciated. TYA.

This is the result of a test run (from Eclipse via Rin as > Testng suite).
For each failed test this is what is displayed:

java.lang.IllegalStateException: The element By.id: usernameParentField is not locatable anymore because its context has been garbage collected
at io.appium.java_client.pagefactory.AppiumElementLocator.lambda$findElement$0(AppiumElementLocator.java:154)
at java.base/java.util.Optional.orElseThrow(Optional.java:403)
at io.appium.java_client.pagefactory.AppiumElementLocator.findElement(AppiumElementLocator.java:153)
at io.appium.java_client.pagefactory.interceptors.InterceptorOfASingleElement.call(InterceptorOfASingleElement.java:64)
at io.appium.java_client.proxy.Interceptor.intercept(Interceptor.java:78)
at org.openqa.selenium.remote.RemoteWebElement$ByteBuddy$9yldCNC4.click(Unknown Source)
at dbp_TA_Mobile.pageObjects.android.LoginPageForm.setUsername(LoginPageForm.java:52)
at dbp_TA_Mobile.ASB_AppMobileTesting.Mobile_Smoke_ChangeContactInfoAndVerify.ChangeContactInfoAndVerifyTest(Mobile_Smoke_ChangeContactInfoAndVerify.java:21)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at org.testng.internal.invokers.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:139)
at org.testng.internal.invokers.TestInvoker.invokeMethod(TestInvoker.java:664)
at org.testng.internal.invokers.TestInvoker.invokeTestMethod(TestInvoker.java:227)
at org.testng.internal.invokers.MethodRunner.runInSequence(MethodRunner.java:50)
at org.testng.internal.invokers.TestInvoker$MethodInvocationAgent.invoke(TestInvoker.java:957)
at org.testng.internal.invokers.TestInvoker.invokeTestMethods(TestInvoker.java:200)
at org.testng.internal.invokers.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:148)
at org.testng.internal.invokers.TestMethodWorker.run(TestMethodWorker.java:128)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
at org.testng.TestRunner.privateRun(TestRunner.java:848)
at org.testng.TestRunner.run(TestRunner.java:621)
at org.testng.SuiteRunner.runTest(SuiteRunner.java:443)
at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:437)
at org.testng.SuiteRunner.privateRun(SuiteRunner.java:397)
at org.testng.SuiteRunner.run(SuiteRunner.java:336)
at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:52)
at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:95)
at org.testng.TestNG.runSuitesSequentially(TestNG.java:1280)
at org.testng.TestNG.runSuitesLocally(TestNG.java:1200)
at org.testng.TestNG.runSuites(TestNG.java:1114)
at org.testng.TestNG.run(TestNG.java:1082)
at org.testng.remote.AbstractRemoteTestNG.run(AbstractRemoteTestNG.java:115)
at org.testng.remote.RemoteTestNG.initAndRun(RemoteTestNG.java:251)
at org.testng.remote.RemoteTestNG.main(RemoteTestNG.java:77)

This is my test class:

public class Mobile_Smoke_LoginAppASB extends AppiumConfTest {

	@Test 
	public void LoginTest() throws MalformedURLException, InterruptedException {

		LoginPageForm loginPageForm = new LoginPageForm(driver);
		loginPageForm.setUsername("testerpro1");
		loginPageForm.setPassword("Pa$$word8");

		InstantBalScrForm instantBalScrForm = new InstantBalScrForm(driver);
		instantBalScrForm.skipScreen();

		WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(80));
		wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("(//*[@text='Accounts'])")));

		Assert.assertTrue(
				driver.findElement(By.id("com.lorem.ipsum.integrationtwo:id/webViewTitleTextView"))
						.isDisplayed());

	}

	@AfterClass
	public void LogOutAsb() throws InterruptedException {

		ProfileIconTooltip profileIconTooltip = new ProfileIconTooltip(driver);
		profileIconTooltip.signOutAction();

	}
}

This is my page object class:

public class LoginPageForm extends AndroidActions{

	AndroidDriver driver;
	
	public LoginPageForm(AndroidDriver driver) 
	{
		super(driver);
		this.driver = driver;
		PageFactory.initElements(new AppiumFieldDecorator(driver), this);
		
	}
	
	@AndroidFindBy(xpath="//android.widget.RelativeLayout") 
	//@AndroidFindBy(id="com.lorem.ipsum.integrationtwo:id/text_input_layout") 
	private WebElement usernameParentField;
	
	@AndroidFindBy(id="com.lorem.ipsum.integrationtwo:id/edit_text") 
	private WebElement usernameEditField;
	
	@AndroidFindBy(id="com.lorem.ipsum.integrationtwo:id/text_input_layout") 
	private WebElement usernameInputField;
	
	@AndroidFindBy(xpath="//*[@text='Enter your username']") 
	private WebElement usernameTextLocation;
	
	@AndroidFindBy(xpath="//*[@text='Enter password']") 
	private WebElement passwordTextLocation;
	
	@AndroidFindBy(id="com.lorem.ipsum.integrationtwo:id/button_action") 
	private WebElement submitButton;
	
	@AndroidFindBy(id="com.lorem.ipsum.integrationtwo:id/logo") 
	private WebElement logoImage;

	public void setUsername(String username)
	{
		usernameParentField.click(); 
		usernameEditField.click();
		usernameInputField.click();
		usernameTextLocation.sendKeys(username);
		
	}

	public void setPassword(String password) throws InterruptedException 
	{
		passwordTextLocation.sendKeys(password);
		driver.hideKeyboard();
		WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(60));
		wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("com.lorem.ipsum.integrationtwo:id/button_action")));
		submitButton.click();
	}
}

Configuration (base) class:

public class AppiumConfTest extends AppiumUtils {

    public AndroidDriver driver;
    public AppiumDriverLocalService service;
    
    @BeforeSuite (alwaysRun=true)
    public void setUpAppium() throws IOException {

    	Properties prop = new Properties();
    	FileInputStream fis = new FileInputStream(System.getProperty("user.dir")+"//src//main//java//resources//data.properties");
    	prop.load(fis);
    	String ipAddress = System.getProperty("ipAddress");
    	String port = prop.getProperty("port");
    	   
    	service = startAppiumServer(ipAddress,Integer.parseInt(port)); 
       
    	UiAutomator2Options options = new UiAutomator2Options();
    	options.setDeviceName(prop.getProperty("AndroidDeviceName")); //emulator
    	options.setApp(System.getProperty("user.dir")+"//src//test//java//resources//app-qa.apk");
    	options.setCapability("autoGrantPermissions", "true");
    	
    	driver = new AndroidDriver(service.getUrl(), options);
    	driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(80));
        	
    }

    @AfterSuite (alwaysRun=true)
    public void tearDown() {
        driver.quit();
        service.stop();
        }    
}

Here is pom:

4.0.0 TA_Mobile ASB_AppMobileTesting 0.0.1-SNAPSHOT ASB_AppMobileTesting http://www.example.com UTF-8 17 io.appium java-client 8.6.0 org.testng testng 7.8.0 test org.seleniumhq.selenium selenium-support 4.11.0 org.apache.maven.plugins maven-dependency-plugin 3.6.1 org.seleniumhq.selenium selenium-remote-driver 4.13.0 com.aventstack extentreports 5.1.0 commons-io commons-io 2.13.0 maven-clean-plugin 3.3.1 maven-resources-plugin 3.3.1 maven-compiler-plugin 3.11.0 maven-surefire-plugin 3.1.2 testng.xml maven-jar-plugin 3.3.0 maven-install-plugin 3.1.1 maven-deploy-plugin 3.1.1 maven-site-plugin 4.0.0-M10 maven-project-info-reports-plugin 3.4.5

Here is testng.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name="Suite">
<test name="Mobile_Smoke_LoginAppASB">
<classes>
<class name="TA_Mobile.ASB_AppMobileTesting.Mobile_Smoke_LoginAppASB"/>
</classes>
</test>
<!--  Test  -->
<test name="Mobile_Smoke_ViewTransfersScreen">
<classes>
<class name="TA_Mobile.ASB_AppMobileTesting.Mobile_Smoke_ViewTransfersScreen"/>
</classes>
</test>
<!--  Test  -->
<test name="Mobile_Smoke_ChangeContactInfoAndVerify">
<classes>
<class name="TA_Mobile.ASB_AppMobileTesting.Mobile_Smoke_ChangeContactInfoAndVerify"/>
</classes>
</test>
<!--  Test  -->
<test name="Mobile_Smoke_ViewMoneyMonitorScreen">
<classes>
<class name="TA_Mobile.ASB_AppMobileTesting.Mobile_Smoke_ViewMoneyMonitorScreen"/>
</classes>
</test>
<!--  Test  -->
<test name="Mobile_Smoke_SignOutCheck">
<classes>
<class name="TA_Mobile.ASB_AppMobileTesting.Mobile_Smoke_SignOutCheck"/>
</classes>
</test>
<!--  Test  -->
<test name="Mobile_Smoke_SetUpCustomAlertAndVerify">
<classes>
<class name="TA_Mobile.ASB_AppMobileTesting.Mobile_Smoke_SetUpCustomAlertAndVerify"/>
</classes>
</test>
<!--  Test  -->
<test name="Mobile_Smoke_ViewDepositCheckScreen">
<classes>
<class name="TA_Mobile.ASB_AppMobileTesting.Mobile_Smoke_ViewDepositCheckScreen"/>
</classes>
</test>
<!--  Test  -->
<test name="Mobile_Smoke_ViewCheckingAccount">
<classes>
<class name="TA_Mobile.ASB_AppMobileTesting.Mobile_Smoke_ViewCheckingAccount"/>
</classes>
</test>
<!--  Test  -->
</suite>
<!--  Suite  -->
1 Like

I faced the same issue and I don’t know what is the solution?

Hello @NatSam @mykola-mokhnach @Mary_Shokry I am facing the exact same issue for past 10 days and cannot find a solution

Here is my BaseTestClass

package com.saral.application.BaseTest;

import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.android.options.UiAutomator2Options;
import io.appium.java_client.service.local.AppiumDriverLocalService;
import io.appium.java_client.service.local.AppiumServiceBuilder;
import io.appium.java_client.service.local.flags.GeneralServerFlag;
import io.qameta.allure.Step;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.io.FileHandler;
import org.testng.annotations.AfterSuite;
import org.testng.annotations.BeforeSuite;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.time.Duration;

public class SaralBaseTest {
    private final UiAutomator2Options options = new UiAutomator2Options();
    protected static AndroidDriver driver;
    AppiumDriverLocalService service;

    /**
     * Setup method to start driver and use UIAutomator2 options
     */
    @BeforeSuite
    @Step("Start Emulator and Set Options and Capabilities")
    public void setup() {
        try {
            AppiumServiceBuilder builder = new AppiumServiceBuilder();
            builder.withIPAddress("127.0.0.1")
                    .usingPort(4723)
                    .withArgument(GeneralServerFlag.SESSION_OVERRIDE)
                    .withArgument(GeneralServerFlag.LOG_LEVEL, "debug");
            service = AppiumDriverLocalService.buildService(builder);
            service.start();
            options.setAvd("DemoDevice01")
                    .setAvdLaunchTimeout(Duration.ofSeconds(20)) // Duration to wait for AVD to launch
                    .setAvdReadyTimeout(Duration.ofSeconds(20)) // Duration to wait for AVD to be Ready
                    .setPlatformVersion("7.0") // Android Version
                    .setApp("C:\\saral_android_automation_java\\src\\test\\resources\\SaralApp.apk") // App to test
                    .noReset()
                    .ensureWebviewsHavePages()
                    .nativeWebScreenshot()
                    .setNewCommandTimeout(Duration.ofSeconds(60))
                    .clearSystemFiles()
                    .setIsHeadless(false);
            driver = new AndroidDriver(new URL("http://127.0.0.1:4723"), options);
        } catch (MalformedURLException e) {
            e.printStackTrace();
        }
    }

    /**
     * Teardown Method
     */
    @AfterSuite
    @Step("Quit the driver")
    public void teardown() {
        driver.quit();
        service.stop();
    }

    /**
     * TakeScreenShot() method to take screenshot if tests fail
     */
    public static void takeScreenshot() {
        try {
            File file = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
            String currentDir = System.getProperty("user.dir") + "/build/screenshots/";
            FileHandler.copy(file, new File(currentDir + System.currentTimeMillis() + ".png"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Here is my LoginPage class

package com.saral.application.pages;

import com.saral.application.BaseTest.SaralBaseTest;
import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.pagefactory.AndroidBy;
import io.appium.java_client.pagefactory.AppiumFieldDecorator;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.PageFactory;

import java.time.Duration;

public class LoginPage extends SaralBaseTest {
    @AndroidBy(id = "com.saral.application:id/et_mobile")
    WebElement Phone;

    @AndroidBy(className = "android.widget.Button")
    WebElement Next;

    @AndroidBy(id = "com.saral.application:id/otp_view")
    WebElement OTP;

    @AndroidBy(className = "android.widget.Button")
    WebElement Submit;

    @AndroidBy(xpath = "//android.widget.FrameLayout[@content-desc='More']/android.widget.FrameLayout/android.widget.ImageView")
    WebElement MoreButton;

    @AndroidBy(id = "com.saral.application:id/iv_arrow")
    WebElement SettingDropDown;

    @AndroidBy(id = "com.saral.application:id/cl_logout")
    WebElement Logout;

    @AndroidBy(id = "android:id/button1")
    WebElement Yes;

    public LoginPage(AndroidDriver driver) {
        SaralBaseTest.driver = driver;
        PageFactory.initElements(new AppiumFieldDecorator(driver, Duration.ofSeconds(10)), this);
    }

    /**
     * Login into the app
     * @param phone phone number
     * @param otp OTP
     */
    public void Login(String phone, String otp) {
        try {
            if (Phone.isDisplayed()) {
                Phone.sendKeys(phone);
            }
            if (Next.isDisplayed()) {
                Next.click();
            }
            if (OTP.isDisplayed()) {
                OTP.sendKeys(otp);
            }
            if (Submit.isDisplayed()) {
                Submit.click();
            }
        } catch (NoSuchElementException e) {
            SaralBaseTest.takeScreenshot();
            e.printStackTrace();
        }
    }

    /**
     Logout of the app
     */
    public void Logout() {
        try {
            if (MoreButton.isDisplayed()) {
                MoreButton.click();
            }
            if (SettingDropDown.isDisplayed()) {
                SettingDropDown.click();
            }
            if (Logout.isDisplayed()) {
                Logout.click();
            }
            if (Yes.isDisplayed()) {
                Yes.click();
            }
        } catch (NoSuchElementException e) {
            SaralBaseTest.takeScreenshot();
            e.printStackTrace();
        }

    }
}

Here is my LoginTest class

package com.saral.application.tests;

import com.saral.application.BaseTest.SaralBaseTest;
import com.saral.application.pages.LoginPage;
import io.qameta.allure.Description;
import org.testng.annotations.Test;

public class LoginTest extends SaralBaseTest {
    LoginPage loginPage = new LoginPage(driver);

    @Test
    @Description("Login into app")
    public void Login() {
        loginPage.Login("8287210847", "010203");
    }

    @Test
    @Description("Logout of App")
    public void Logout() {
        loginPage.Logout();
    }
}

This is the error I am getting

java.lang.IllegalStateException: The element By.id: Phone is not locatable anymore because its context has been garbage collected
	at io.appium.java_client.pagefactory.AppiumElementLocator.lambda$findElement$0(AppiumElementLocator.java:154)

please let me know if you have a solution

hi @Sparsh_Goyal,

You need to initialize it in BaseClass first, such as this.

then you can just call it in the test class.

do let me know if it helps.