Just want to get clarity on the driver type that I need to use for testing mobile app on Android and iOS devices. My main goal is to keep the same code-base for the two platforms - although, keeping them separate would be easier to implement; but difficult to maintain.
Having said that, I am curious if the following code snippet will work in this situation.
AppiumDriver<MobileElement> driver = null;
if ("iOS".equals(os)) {
driver = new IOSDriver<MobileElement>(new URL(urlString), capabilities);
} else {
driver = new AndroidDriver<MobileElement>(new URL(urlString), capabilities);
}
Yes the code snippet which you have mentioned will work for your situation.
AppiumDriver<MobileElement> driver = null;
if ("iOS".equals(os)) {
driver = new IOSDriver<MobileElement>(new URL(urlString), capabilities);
} else {
driver = new AndroidDriver<MobileElement>(new URL(urlString), capabilities);
}
The driver instance created will be of type MobileElement.
private AppiumDriver<AppiumWebElement> _driver;
public readonly string platform = "iOS";
switch (platform)
{
case MobilePlatform.Android:
_driver = new AndroidDriver<AppiumWebElement>(new
Uri("http://localhost:4723/wd/hub"), driverOptions,
TimeSpan.FromSeconds(300));
break;
case MobilePlatform.IOS:
_driver = new IOSDriver<AppiumWebElement>(new
Uri("http://127.0.0.1:4723/wd/hub"), driverOptions,
TimeSpan.FromSeconds(300));
break;
}
Related
I want to implement a method to detect type of device connected and run it's specified script any help?
This will get you the device for Android and iOS. Do whatever you want to do from there.
In Android:
String manufacturer = Build.MANUFACTURER;
String model = Build.MODEL;
In iOS:
https://stackoverflow.com/a/26962452/8200290
I created enum and method to differentiate between both types
public static OS detectOperatingSystem ()
{if (OSName.contains("android")) {
return OS.ANDROID;
}
if (OSName.contains("ios")) {
return OS.IOS;
}
else return null;
}
String = OSName;
public enum OS {IOS,ANDROID}
}
#BeforeClass
public void getDesiredCapabilites() throws MalformedURLException {
switch (detectOperatingSystem()) {
case ANDROID:
capabilities.setCapability(MobileCapabilityType.DEVICE_NAME, androidVirtualDeviceVersion);
capabilities.setCapability(MobileCapabilityType.PLATFORM_NAME, androidMobilePlatform);
capabilities.setCapability(MobileCapabilityType.PLATFORM_VERSION, androidVirtualDeviceVersion);
capabilities.setCapability(MobileCapabilityType.AUTOMATION_NAME, androidAutomationName);
capabilities.setCapability(MobileCapabilityType.APP, androidFilePath);
capabilities.setCapability("eventTimings", true);
//capabilities.setCapability("unicodeKeyboard", true);
setDriver();
break;
case IOS:
capabilities.setCapability(MobileCapabilityType.DEVICE_NAME, IOSDeviceName);
capabilities.setCapability(MobileCapabilityType.PLATFORM_NAME, IOSMobilePlatform);
capabilities.setCapability(MobileCapabilityType.PLATFORM_VERSION, IOSPlatformVersion);
capabilities.setCapability(MobileCapabilityType.AUTOMATION_NAME, IOSAutomationName);
capabilities.setCapability(MobileCapabilityType.APP, IOSFilePath);
setDriver();
//IOSdriver = new IOSDriver(new URL("http://127.0.0.1:4723/wd/hub"), capabilities);
break;
}
A week ago Norton Security started to find Trojan horse (Trojan.Gen.NPE.2) in file R.class located:
app\build\intermediates\transforms\instantrun\debug\folders\1\5\main\android\support\v4\ r.class
and
app\build\intermediates\transforms\instantrunslicer\debug\folders\1\5\slice_2\android\support\v4\ r.class
Link to virustotal.com scan: https://www.virustotal.com/pl/file/282f4e09c102cd93c2f849c9fe9ed570188f5a03395ab84d2457a7c64d7acb1c/analysis/1499458308/
And the code:
package android.support.v4;
import com.android.tools.fd.runtime.IncrementalChange;
import com.android.tools.fd.runtime.InstantReloadException;
public final class R {
public static final long serialVersionUID = 830508538663712626L;
public R() {
IncrementalChange var1 = $change;
if(var1 != null) {
Object[] var10001 = (Object[])var1.access$dispatch("init$args.([Landroid/support/v4/R;[Ljava/lang/Object;)Ljava/lang/Object;", new Object[]{null, new Object[0]});
Object[] var2 = (Object[])var10001[0];
this(var10001, (InstantReloadException)null);
var2[0] = this;
var1.access$dispatch("init$body.(Landroid/support/v4/R;[Ljava/lang/Object;)V", var2);
} else {
super();
}
}
R(Object[] var1, InstantReloadException var2) {
String var3 = (String)var1[1];
switch(var3.hashCode()) {
case -1968665286:
super();
return;
case -71951640:
this();
return;
default:
throw new InstantReloadException(String.format("String switch could not find \'%s\' with hashcode %s in %s", new Object[]{var3, Integer.valueOf(var3.hashCode()), "android/support/v4/R"}));
}
}
}
Is it false alarm?
I cant add exclusions in Symantec on my laptop as its company's laptop and excluding applications, files or folders feature is deactivated. So in order to get going i have disabled instant run to get going else i can't even install app on my emulator and get stuck with installation errors.
You can disable instant run by unchecking instant run in Settings in Android Studio.
NOTE: This is just workaround and not permanent solution.
You can report this false positive to Symantec and have them fix it.
https://submit.symantec.com/false_positive/
I am learning to work with google cloud endpoint. I am trying to connect from my device however, I keep getting an error.
failed to connect to /192.168.1.100 (port 8080) after 20000ms: isConnected failed: ECONNREFUSED (Connection refused)
How do I overcome this issue, I would like to run on a real device?
Get Jokes.class
public class GetJokes extends AsyncTask<Pair<Context, String>, Void, String> {
private static MyApi myApiService = null;
private Context context;
#Override
protected String doInBackground(Pair<Context, String>... params) {
if (myApiService == null) { // Only do this once
MyApi.Builder builder = new MyApi.Builder(AndroidHttp.newCompatibleTransport(),
new AndroidJsonFactory(), null)
// options for running against local devappserver
// - 10.0.2.2 is localhost's IP address in Android emulator
// - turn off compression when running against local devappserver
.setRootUrl("http://10.0.2.2:8080/_ah/api/")
.setGoogleClientRequestInitializer(new GoogleClientRequestInitializer() {
#Override
public void initialize(AbstractGoogleClientRequest<?> abstractGoogleClientRequest) throws IOException {
abstractGoogleClientRequest.setDisableGZipContent(true);
}
});
// end options for devappserver
myApiService = builder.build();
}
context = params[0].first;
String name = params[0].second;
try {
return myApiService.sayHi(name).execute().getData();
} catch (IOException e) {
return e.getMessage();
}
}
#Override
protected void onPostExecute(String result) {
Toast.makeText(context, result, Toast.LENGTH_LONG).show();
Log.i("Failed to con", result);
}
}
MainActivityFragment.class
public class MainActivityFragment extends Fragment {
public static final String JOKE = "JOKE";
public MainActivityFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_main, container, false);
Button tellJokeButton = (Button)root.findViewById(R.id.tellJoke_button);
tellJokeButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
new GetJokes().execute(new Pair<Context, String>(getActivity(), "Manfred"));
}
});
AdView mAdView = (AdView) root.findViewById(R.id.adView);
// Create an ad request. Check logcat output for the hashed device ID to
// get test ads on a physical device. e.g.
// "Use AdRequest.Builder.addTestDevice("ABCDEF012345") to get test ads on this device."
AdRequest adRequest = new AdRequest.Builder()
.addTestDevice(AdRequest.DEVICE_ID_EMULATOR)
.build();
mAdView.loadAd(adRequest);
return root;
}
public void tellJoke(){
Intent intent = new Intent(getActivity(), JokeTellerActivity.class);
//TODO: get jokes from Google Cloud endpoint
intent.putExtra(JOKE,
getActivity().getString(R.string.joke)
);
startActivity(intent);
}
}
MyEndPoint.class
public class MyEndpoint {
public static List<Joke> mJokes = new ArrayList<>();
/** A simple endpoint method that takes a name and says Hi back */
#ApiMethod(name = "sayHi")
public MyBean sayHi(#Named("name") String name) {
MyBean response = new MyBean();
response.setData("Hi, " + name);
return response;
}
}
I've tried running it on several different devices and none of them would connect.
I've had the same problem because I'm also doing this project from the Android Nanodegree (Udacity).
I've found the solution in this question. Because the answers there are a bit messy or uncomplete, I'm going to explain in detail what I did to run the local server on my computer and test it with a real device.
1) Add httpAddress = "0.0.0.0" to the appengine block in your build.gradle file of the backend (Google Cloud Endpoint) module, like this:
appengine {
...
httpAddress = "0.0.0.0"
}
According to a comment on the linked question this means that 'will accept from anywhere'.
2) Run your backend module locally (ie. start the web server). This is trivial: select the backend configuration on the drop-down menu and press the green button. You can find more detailed instructions here.
You should now be able to open a browser (on your computer) and navigate to http://localhost:8080/ and see a webpage with 'Hello, Endpoints!' and many other stuff.
3) Find your computer's IP address (on a Mac go to System Preferences -> Network) and then set it as the root url in your Android app code, like this:
MyApi.Builder builder = new MyApi.Builder(
AndroidHttp.newCompatibleTransport(),
new AndroidJsonFactory(),
null
).setRootUrl("http://192.168.1.36:8080/_ah/api/")
That's all!
So you've setup an AppEngine based cloud endpoint as a local development server on the same machine in which you are running an Android emulator? Be sure you're using the correct IP address (and port) for the endpoint. The local IP address you need is not the one in use by the Android emulator, but the host dev machine. For example:
Local Dev Machine: 192.168.1.100
AppEngine server on dev machine: listening on 8080
Android Emulator running on dev machine: 10.0.2.2
You should be able to access the API explorer on the dev machine via a browser on the Android emulator as well. You can use that to sanity check that your app is using the correct address:
http://192.168.1.100:8080/_ah/api/explorer
I am trying to use Appium to automate a test case on my app.
I managed to run a simple script, but I do NOT understand the logic of of the multiple testcases running process like android life-cycle.
What is the cycle for a testcase?
Because when I run the code below it does not run in order of: firstTest, secondTest, thirdTest...
How do we tell the testCase what to run first and in what order ? thanks
public class LoginTest {
AndroidDriver driver;
#BeforeClass
public void setUp() throws MalformedURLException{
DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.setCapability("device", "Android");
capabilities.setCapability(CapabilityType.BROWSER_NAME, ""); //Name of mobile web browser to automate. Should be an empty string if automating an app instead.
capabilities.setCapability(CapabilityType.VERSION, "5.0.2");
capabilities.setCapability(CapabilityType.PLATFORM, "Android");
capabilities.setCapability("app-package", "com.myapp"); //Replace with your app's package
capabilities.setCapability("app-activity", ".myapp"); //Replace with app's Activity
driver = new RemoteWebDriver(new URL("http://127.0.0.1:4723/wd/hub"), capabilities);
}
#Test
public void firstTest() throws InterruptedException
{
List<WebElement> textFieldsList = driver.findElements(By.className("android.widget.EditText"));
int size = textFieldsList.size();
textFieldsList.get(0).sendKeys("test#test.com");
textFieldsList.get(1).sendKeys("12345");
Thread.sleep(1000);
WebElement btnLogin=driver.findElement(By.name("Login"));
String login = btnLogin.getText();
Assert.assertTrue(login.contains("Login"));
System.out.println(login);
btnLogin.click();
Thread.sleep(1000);
}
#Test
public void secondTest() throws InterruptedException {
WebElement btnHome=driver.findElement(By.name("Home"));
String login_1 = btnHome.getText();
Assert.assertTrue(login_1.contains("Home"));
System.out.println(login_1);
btnHome.click();
Thread.sleep(1000);
}
#Test
public void thirdTest() throws InterruptedException {
WebElement btnSecond=driver.findElement(By.name("Second"));
String login_2 = btnSecond.getText();
Assert.assertTrue(login_2.contains("Second"));
System.out.println(login_2);
btnSecond.click();
Thread.sleep(1000);
}
#AfterClass
public void tearDown() {
driver.quit();
}
Thank you
Well the answer to this depends on the Test framework you are using to for your Test.
If you are using Junit for your tests, then you might not be able to prioritise them in a user defined order.
On the other end using if you are using TestNG framework, adding parameter to Test annotation would solve your problem. e.g.
#Test(groups = {"checklist1"}, priority = 1, testName = "firstTest", description = "My First Test")
Though I would suggest, you go through and follow this.
You can use dependsOnMethods with #Test annotation to make that flow:
#Test(dependsOnMethods = "methodName")
We are using the built-in DownloadManager to grab files from our server. If we figure out that there has been an update to that file we delete the local version and re-queue a download from the DownloadManager. This only runs when you fully kill and re-start the app (timely updates to files are not the priority, just that we have all the files and that they get updated whenever we notice it). This system works perfectly on all of my personal testing devices, however, when testing in the api 19 emulator or on my co-worker's HTC One the files will download and then disappear (no longer in the app's external data folder). I've figured out that both are version 4.4.2 of android (where my devices are either 4.4.4 or 4.0.4). It's weird because they will stick around for a time, but then random files will disappear.
Here is some code:
AssetManager setup (setup of output folder)
private AssetManager(Context activity){
if(singleton != null&&IOUtils.hasExternalStorage() != IOUtils.ExtStorageState_OK){
return;
}
context = activity;
external = ContextCompat.getExternalFilesDirs(context, "")[0];
external.mkdirs();
imageFolder = new File(external,imagePath);
imageFolder.mkdirs();
singleton = this;
}
Download code
private static class DownloadObject {
public String ServerID;
public String updated_at;
public Uri image;
public DownloadObject() {
super();
}
public DownloadObject(String ServerID,String updated_at){
super();
this.ServerID = ServerID;
this.updated_at = updated_at;
}
public DownloadObject(Cursor cursor){
super();
this.ServerID = cursor.getString(cursor.getColumnIndex(ObjectDao.Properties.ServerID.columnName));
this.updated_at = cursor.getString(cursor.getColumnIndex(ObjectDao.Properties.UpdatedAt.columnName));
String imageFile = cursor.getString(cursor.getColumnIndex(ObjectDao.Properties.Image.columnName));
this.image = Uri.parse(AssetManager.getSingleton().getImageFolder().getPath()).buildUpon().appendPath(imageFile).scheme("file").build();
}
}
//downloadObjectVector is the fresh list of all objects from the server
//existingObjects is the Cursor from the db that lists all existing object locally
private void SpinOffDownloads(final Vector<DownloadObject> downloadObjectVector,final Cursor existingObjects){
new Thread(new Runnable() {
#Override
public void run() {
int count = 0;
if(existingObjects != null){
count = existingObjects.getCount();
}
if (count>0){
existingObjects.moveToFirst();
do{
final DownloadObject obj = new DownloadObject(existingObjects);
DownloadObject notNeededObject = ArrayUtils.findFirst(downloadObjectVector,new ArrayUtils.Predicate<DownloadObject>() {
#Override
public boolean evaluate(DownloadObject downloadObject) {
return downloadObject.ServerID.equals(obj.ServerID)&&downloadObject.updated_at.compareTo(obj.updated_at) <= 0;
}
});
if (notNeededObject != null){
File imageTest = null;
if(notNeededObject.image != null) {
Uri out = Uri.parse(AssetManager.getSingleton().getImageFolder().getPath()).buildUpon().appendPath(notNeededObject.image.getLastPathSegment()).scheme("file").build();
imageTest = new File(out.getPath());
}else{
Log.v(CLASS_NAME,"object with null image:"+notNeededObject.ServerID);
}
if (imageTest == null||imageTest.exists()) {
downloadObjectVector.remove(notNeededObject);
}else{
if (imageTest != null&&imageTest.exists()&&SHOULD_REPLACE_FILE){
Log.v(CLASS_NAME,"DELETING FILE(missing image):"+imageTest.getAbsolutePath());
imageTest.delete();
}
}
}else{
File imageTest = null;
if(obj.image != null) {
imageTest = new File(obj.image.getPath());
if (imageTest != null&&imageTest.exists()&&SHOULD_REPLACE_FILE){
Log.v(CLASS_NAME,"DELETING FILE(image):"+imageTest.getAbsolutePath());
imageTest.delete();
}
}else{
Log.v(CLASS_NAME,"object with null image:"+obj.ServerID);
}
}
}while(existingObjects.moveToNext());
}
if (existingObjects!= null){
try{
existingObjects.close();
}catch (Exception e){
}
}
DownloadManager dm = (DownloadManager)getSystemService(DOWNLOAD_SERVICE);
for (int i = 0; i < downloadObjectVector.size(); i++) {
try {
DownloadObject dlObj = downloadObjectVector.get(i);
Uri in = dlObj.image;
Uri out = Uri.parse(AssetManager.getSingleton().getImageFolder().getPath()).buildUpon().appendPath(in.getLastPathSegment()).scheme("file").build();
dm.enqueue(new DownloadManager.Request(in).setDestinationUri(out).setTitle(in.getLastPathSegment()));
}catch (Exception e){
Log.w(CLASS_NAME,"Error with Download queued:",e);
}
}
}
}).start();
}
Please let me know if you need any other information or code!
EDIT1
So I decided to elaborate on this a bit more with my testing for this and how the issue manifests itself in the hopes that it will make the picture that much more clear!
I start by loading the app via Android Studio and letting it run long enough to know that all the downloads finish and then I look through the app to see which images are there and which are missing. Most images are there normally. Next I exit the app and use the android task manager to fully kill it. Then I re-launch the app via Android Studio. I then wait to make sure that the downloads finish and watch the LogCat to see what files get deleted manually(normally a couple at maximum). Then I go through the app as see which images are still there/which have been added. It seems that every time new images appear AND new images disappear... And normally the ones that get marked as manually deleted actually get replaced via download properly(i.e. NOT "disappeared").
Please let me know if there are any tests you would like for me to do!
File Observer Test
First of all this is the first time I've used a FileObserver so if I've done something stupid please point it out. Here is my observer code:
external = ContextCompat.getExternalFilesDirs(context, null)[0];
external.mkdirs();
fileObserver = new FileObserver(external.getPath(),FileObserver.ALL_EVENTS) {
#Override
public void onEvent(final int event, final String relPath) {
String msg = "???";
switch (event){
case FileObserver.DELETE:
msg = "FILEOB DELETE relPath:"+relPath;
break;
case FileObserver.DELETE_SELF:
msg = "FILEOB DELETE_SELF relPath:"+relPath;
break;
case FileObserver.MODIFY:
msg = "FILEOB MODIFY relPath:"+relPath;
break;
case FileObserver.MOVE_SELF:
msg = "FILEOB MOVE_SELF relPath:"+relPath;
break;
case FileObserver.MOVED_TO:
msg = "FILEOB MOVED_TO relPath:"+relPath;
break;
case FileObserver.MOVED_FROM:
msg = "FILEOB MOVED_FROM relPath:"+relPath;
break;
case FileObserver.ATTRIB:
msg = "FILEOB ATTRIB relPath:"+relPath;
break;
case FileObserver.CREATE:
msg = "FILEOB CREATE relPath:"+relPath;
break;
default:
msg = "Unknown event:"+event+" at relPath:"+relPath;
}
fileObserverHandler.publish(new LogRecord(Level.INFO,msg));
fileObserverHandler.flush();
}
#Override
public void startWatching() {
super.startWatching();
fileObserverHandler.publish(new LogRecord(Level.INFO,"START WATCHING!!!!"));
fileObserverHandler.flush();
Log.v("FileObserver","START WATCHING!!!");
}
};
fileObserver.startWatching();
I'm using the handler because at first I didn't have the startWatching() override in and wasn't getting any logging at all and the docs say that onEvent happens on its own thread and therefore you should use a handler. It's simply this in the class:
public static Handler fileObserverHandler = new ConsoleHandler();
The ONLY output I get from this at all is "START WATCHING!!!". So I'm guessing I must have done something wrong, because I see it downloading/deleting things... at least it says it is.
The behavior you describe sounds like the system is clearing up those files like a cache.
In your call to getExternalFilesDirs you use "", trying to creating a File/directory with "" can be problematic.
Use null instead of "" in your call to getExternalFilesDirs see if that helps
replace
external = ContextCompat.getExternalFilesDirs(context, "")[0];
with
external = ContextCompat.getExternalFilesDirs(context, null)[0];
It seems this issue may not be related to version 4.4.2 only. After reviewing the Download code over and over, I noticed that the download request does not have a setMimeType setting. Sometimes it appears that DownloadManager deletes files upon completion without setting mime type to download request, on some occasions. By default the server sends the file as its content type as application/x-download. Try adding something like
setMimeType(application/octet-stream);
to DownloadManager.Request(in) or whichever mime type that suits the files being downloaded. Hope this helps.
I think it isn't a problem related with the application logic, but the device you were testing on. I have a tablet with the same problem and I was going crazy... the internal storage (were I save the files) may be damaged...