diff --git a/robot-pattern/README.md b/robot-pattern/README.md new file mode 100644 index 000000000000..98144977628d --- /dev/null +++ b/robot-pattern/README.md @@ -0,0 +1,77 @@ +--- +layout: pattern +title: Robot Pattern +folder: robot-pattern +permalink: /patterns/pipeline/ +categories: Testing +language: en +tags: + - UI testing +--- + +## Intent + +Robot design pattern intends to separate the "how" of UI testing from the "what". When testing the UI, +the purpose should be to test how the view works, and not explicitly test the fields and their values. + +## Explanation + +Robot design pattern suggests building a "testing robot" for each UI screen, which is responsible for +finding the relevant buttons and fields on the screen. The test itself should just specify the values and buttons +to be tested - not their locations or logistical details. + +Real world example + +> Suppose a developer wants to test a login screen, they can write a test which specifies the username +and password, along with with the button to be clicked. They don't have to specify the variables +being tested or make assertion statements - the robot should abstract all those details from the +developer + +In plain words + +> Robot design pattern abstracts the "what" of UI testing and lets the developer focus on +the "how" + +Wikipedia says + +> The Robot Pattern was designed by Jake Wharton at Square back in 2016. The power of this pattern +is its ability to create an abstraction layer in order to interact with the UI in a declarative mode. +Once created, it is then possible to perform multiple tests in order to verify our use cases without +boilerplate code, as well as without maintenance problems related to a refactor. + + +**Programmatic Example** + +For a login screen, a standard automated UI test for email and password assertion +would look like - + +```java +onView(withId(R.id.edt_email)).perform(typeText(user), closeSoftKeyboard()); +onView(withId(R.id.edt_pass)).perform(typeText(pass), closeSoftKeyboard()); +onView(withId(R.id.btn_login)).perform(click()); +onView(withId(R.id.tv_result)).check(matches(withText("LOGIN FAILED"))); +``` + +However, the aim of the robot pattern is to abstract the technicalities and represent +the test as this instead - + +```java +new LoginRobot().username("navdeepgill@anu.edu.au").password("sunflowers").login().resultSuccess();``` + +## Class diagram + +![alt text](./etc/robot-pattern.png “Robot pattern class diagram") + +## Applicability + +Use the Robot design pattern when you want to + +* Build automated UI tests for an entire app +* Unsure about the exact location and variables of the UI elements and want +to maintain the longevity of the tests + + +## Credits + +* [UI testing with Espresso](https://guides.codepath.com/android/ui-testing-with-espresso) +* [UI testing in Android](https://codingwithmitch.com/blog/ui-testing-with-espresso-android/) diff --git a/robot-pattern/etc/robot-pattern.png b/robot-pattern/etc/robot-pattern.png new file mode 100644 index 000000000000..35e1fc4a767e Binary files /dev/null and b/robot-pattern/etc/robot-pattern.png differ diff --git a/robot-pattern/pom.xml b/robot-pattern/pom.xml new file mode 100644 index 000000000000..3715127cd7b3 --- /dev/null +++ b/robot-pattern/pom.xml @@ -0,0 +1,67 @@ + + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.26.0-SNAPSHOT + + promise + + + org.junit.jupiter + junit-jupiter-engine + test + + + org.mockito + mockito-core + test + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + + + + com.example.robotdesignpattern.LoginScreen + + + + + + + + + diff --git a/robot-pattern/src/.gradle/4.10.1/fileChanges/last-build.bin b/robot-pattern/src/.gradle/4.10.1/fileChanges/last-build.bin new file mode 100644 index 000000000000..f76dd238ade0 Binary files /dev/null and b/robot-pattern/src/.gradle/4.10.1/fileChanges/last-build.bin differ diff --git a/robot-pattern/src/.gradle/4.10.1/fileContent/fileContent.lock b/robot-pattern/src/.gradle/4.10.1/fileContent/fileContent.lock new file mode 100644 index 000000000000..cd7717b462b2 Binary files /dev/null and b/robot-pattern/src/.gradle/4.10.1/fileContent/fileContent.lock differ diff --git a/robot-pattern/src/.gradle/4.10.1/fileHashes/fileHashes.bin b/robot-pattern/src/.gradle/4.10.1/fileHashes/fileHashes.bin new file mode 100644 index 000000000000..ee68919dfc0a Binary files /dev/null and b/robot-pattern/src/.gradle/4.10.1/fileHashes/fileHashes.bin differ diff --git a/robot-pattern/src/.gradle/4.10.1/fileHashes/fileHashes.lock b/robot-pattern/src/.gradle/4.10.1/fileHashes/fileHashes.lock new file mode 100644 index 000000000000..200c68787614 Binary files /dev/null and b/robot-pattern/src/.gradle/4.10.1/fileHashes/fileHashes.lock differ diff --git a/robot-pattern/src/.gradle/4.10.1/fileHashes/resourceHashesCache.bin b/robot-pattern/src/.gradle/4.10.1/fileHashes/resourceHashesCache.bin new file mode 100644 index 000000000000..a648d1284df0 Binary files /dev/null and b/robot-pattern/src/.gradle/4.10.1/fileHashes/resourceHashesCache.bin differ diff --git a/robot-pattern/src/.gradle/4.10.1/gc.properties b/robot-pattern/src/.gradle/4.10.1/gc.properties new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/robot-pattern/src/.gradle/4.10.1/javaCompile/classAnalysis.bin b/robot-pattern/src/.gradle/4.10.1/javaCompile/classAnalysis.bin new file mode 100644 index 000000000000..72499db1ab95 Binary files /dev/null and b/robot-pattern/src/.gradle/4.10.1/javaCompile/classAnalysis.bin differ diff --git a/robot-pattern/src/.gradle/4.10.1/javaCompile/jarAnalysis.bin b/robot-pattern/src/.gradle/4.10.1/javaCompile/jarAnalysis.bin new file mode 100644 index 000000000000..62d71c05b061 Binary files /dev/null and b/robot-pattern/src/.gradle/4.10.1/javaCompile/jarAnalysis.bin differ diff --git a/robot-pattern/src/.gradle/4.10.1/javaCompile/javaCompile.lock b/robot-pattern/src/.gradle/4.10.1/javaCompile/javaCompile.lock new file mode 100644 index 000000000000..7d0718244ba6 Binary files /dev/null and b/robot-pattern/src/.gradle/4.10.1/javaCompile/javaCompile.lock differ diff --git a/robot-pattern/src/.gradle/4.10.1/javaCompile/taskHistory.bin b/robot-pattern/src/.gradle/4.10.1/javaCompile/taskHistory.bin new file mode 100644 index 000000000000..2eb7640647d3 Binary files /dev/null and b/robot-pattern/src/.gradle/4.10.1/javaCompile/taskHistory.bin differ diff --git a/robot-pattern/src/.gradle/4.10.1/taskHistory/taskHistory.bin b/robot-pattern/src/.gradle/4.10.1/taskHistory/taskHistory.bin new file mode 100644 index 000000000000..bd1c4c4fed8e Binary files /dev/null and b/robot-pattern/src/.gradle/4.10.1/taskHistory/taskHistory.bin differ diff --git a/robot-pattern/src/.gradle/4.10.1/taskHistory/taskHistory.lock b/robot-pattern/src/.gradle/4.10.1/taskHistory/taskHistory.lock new file mode 100644 index 000000000000..08a9e3fd68f7 Binary files /dev/null and b/robot-pattern/src/.gradle/4.10.1/taskHistory/taskHistory.lock differ diff --git a/robot-pattern/src/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/robot-pattern/src/.gradle/buildOutputCleanup/buildOutputCleanup.lock new file mode 100644 index 000000000000..a756104cf0c9 Binary files /dev/null and b/robot-pattern/src/.gradle/buildOutputCleanup/buildOutputCleanup.lock differ diff --git a/robot-pattern/src/.gradle/buildOutputCleanup/cache.properties b/robot-pattern/src/.gradle/buildOutputCleanup/cache.properties new file mode 100644 index 000000000000..bc55b897ef83 --- /dev/null +++ b/robot-pattern/src/.gradle/buildOutputCleanup/cache.properties @@ -0,0 +1,2 @@ +#Sat Oct 29 14:15:17 AEDT 2022 +gradle.version=4.10.1 diff --git a/robot-pattern/src/.gradle/buildOutputCleanup/outputFiles.bin b/robot-pattern/src/.gradle/buildOutputCleanup/outputFiles.bin new file mode 100644 index 000000000000..eb909dca9cbf Binary files /dev/null and b/robot-pattern/src/.gradle/buildOutputCleanup/outputFiles.bin differ diff --git a/robot-pattern/src/.gradle/vcs-1/gc.properties b/robot-pattern/src/.gradle/vcs-1/gc.properties new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/robot-pattern/src/app/.gitignore b/robot-pattern/src/app/.gitignore new file mode 100644 index 000000000000..796b96d1c402 --- /dev/null +++ b/robot-pattern/src/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/robot-pattern/src/app/build.gradle b/robot-pattern/src/app/build.gradle new file mode 100644 index 000000000000..2be97c8eaab0 --- /dev/null +++ b/robot-pattern/src/app/build.gradle @@ -0,0 +1,30 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 28 + defaultConfig { + applicationId "com.example.espressoexample" + minSdkVersion 15 + targetSdkVersion 28 + versionCode 1 + versionName "1.0" + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation 'com.android.support:appcompat-v7:28.0.0' + implementation 'com.android.support.constraint:constraint-layout:1.1.3' + testImplementation 'junit:junit:4.12' + androidTestImplementation 'com.android.support.test:runner:1.0.2' + androidTestImplementation 'com.android.support.test:rules:1.0.2' + androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' + +} diff --git a/robot-pattern/src/app/proguard-rules.pro b/robot-pattern/src/app/proguard-rules.pro new file mode 100644 index 000000000000..f1b424510da5 --- /dev/null +++ b/robot-pattern/src/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/robot-pattern/src/app/src/androidTest/java/com/example/robotdesignpattern/LoginRobot.java b/robot-pattern/src/app/src/androidTest/java/com/example/robotdesignpattern/LoginRobot.java new file mode 100644 index 000000000000..abc29026d706 --- /dev/null +++ b/robot-pattern/src/app/src/androidTest/java/com/example/robotdesignpattern/LoginRobot.java @@ -0,0 +1,43 @@ +package com.example.robotdesignpattern; + +import static android.support.test.espresso.Espresso.onView; +import static android.support.test.espresso.action.ViewActions.click; +import static android.support.test.espresso.action.ViewActions.closeSoftKeyboard; +import static android.support.test.espresso.action.ViewActions.typeText; +import static android.support.test.espresso.assertion.ViewAssertions.matches; +import static android.support.test.espresso.matcher.ViewMatchers.withId; +import static android.support.test.espresso.matcher.ViewMatchers.withText; + +public class LoginRobot { + + public LoginRobot username(String user) { + onView(withId(R.id.edt_email)).perform(typeText(user), closeSoftKeyboard()); + return this; + } + + public LoginRobot password(String pass){ + onView(withId(R.id.edt_pass)).perform(typeText(pass), closeSoftKeyboard()); + return this; + } + + public LoginRobot login(){ + onView(withId(R.id.btn_login)).perform(click()); + return this; + } + + public LoginRobot resultFail(){ + onView(withId(R.id.tv_result)).check(matches(withText("LOGIN FAILED"))); + return this; + + } + + public LoginRobot resultSuccess(){ + onView(withId(R.id.tv_result)).check(matches(withText("LOGIN SUCCESS!"))); + return this; + + } + + + + +} \ No newline at end of file diff --git a/robot-pattern/src/app/src/androidTest/java/com/example/robotdesignpattern/LoginTest.java b/robot-pattern/src/app/src/androidTest/java/com/example/robotdesignpattern/LoginTest.java new file mode 100644 index 000000000000..a3686e3e2e57 --- /dev/null +++ b/robot-pattern/src/app/src/androidTest/java/com/example/robotdesignpattern/LoginTest.java @@ -0,0 +1,31 @@ +package com.example.robotdesignpattern; + +import android.support.test.rule.ActivityTestRule; +import android.support.test.runner.AndroidJUnit4; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static android.support.test.espresso.action.ViewActions.click; +import static android.support.test.espresso.matcher.ViewMatchers.withId; +import static android.support.test.espresso.matcher.ViewMatchers.withText; + +@RunWith(AndroidJUnit4.class) +public class LoginTest { + + @Rule + public ActivityTestRule activityRule + = new ActivityTestRule<>(LoginScreen.class); + + @Test + public void loginFailed() { + new LoginRobot().username("navdeepgill@anu.edu.au").password("roses").login().resultFail(); + } + + @Test + public void loginSuccess() { + new LoginRobot().username("navdeepgill@anu.edu.au").password("sunflowers").login().resultSuccess(); + + } + +} diff --git a/robot-pattern/src/app/src/main/AndroidManifest.xml b/robot-pattern/src/app/src/main/AndroidManifest.xml new file mode 100644 index 000000000000..3b725027f641 --- /dev/null +++ b/robot-pattern/src/app/src/main/AndroidManifest.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/robot-pattern/src/app/src/main/java/com/example/robotdesignpattern/LoginScreen.java b/robot-pattern/src/app/src/main/java/com/example/robotdesignpattern/LoginScreen.java new file mode 100644 index 000000000000..be9a0625e527 --- /dev/null +++ b/robot-pattern/src/app/src/main/java/com/example/robotdesignpattern/LoginScreen.java @@ -0,0 +1,41 @@ +package com.example.robotdesignpattern; + +import android.support.v7.app.AppCompatActivity; +import android.os.Bundle; +import android.view.View; +import android.widget.Button; +import android.widget.EditText; +import android.widget.TextView; + +public class +LoginScreen extends AppCompatActivity { + + EditText Email, Password; + Button Login; + TextView Result; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + Email = findViewById(R.id.edt_email); + Password = findViewById(R.id.edt_pass); + Result = findViewById(R.id.tv_result); + Login = findViewById(R.id.btn_login); + + Login.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + + if (Email.getText().toString().equalsIgnoreCase("navdeepgill@anu.edu.au") + && Password.getText().toString().equalsIgnoreCase("sunflowers")) { + Result.setText("LOGIN SUCCESS!"); + } else { + Result.setText("LOGIN FAILED"); + } + + } + }); + } +} diff --git a/robot-pattern/src/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/robot-pattern/src/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 000000000000..1f6bb290603d --- /dev/null +++ b/robot-pattern/src/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + diff --git a/robot-pattern/src/app/src/main/res/drawable/ic_launcher_background.xml b/robot-pattern/src/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 000000000000..0d025f9bf6b6 --- /dev/null +++ b/robot-pattern/src/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/robot-pattern/src/app/src/main/res/layout/activity_main.xml b/robot-pattern/src/app/src/main/res/layout/activity_main.xml new file mode 100644 index 000000000000..c0ec8db1af94 --- /dev/null +++ b/robot-pattern/src/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,73 @@ + + + + + + + +