Screenshot tests
Main purpose
Sometimes when developing new features, there is a need to check if the application works properly in all supported languages. Manual locale setting changes could take a long time and require the efforts of developers, QA engineers, and etc. Also, it could increase the duration of the localization process.
In order to avoid that, Kaspresso provides DocLocScreenshotTestCase
which allows taking screenshots in all locales you specified. DocLocScreenshotTestCase
extends
default Kaspresso TestCase
and offers the opportunity to make screenshots out the box by
calling DocLocScreenshotTestCase#captureScreenshot(String)
method.
Usage
To create a single test, you should extend DocLocScreenshotTestCase
class as shown below:
@RunWith(AndroidJUnit4::class)
class ScreenshotSampleTest : DocLocScreenshotTestCase(
locales = "en,ru"
) {
@ScreenShooterTest
@Test
fun test() {
before{
}.after {
}.run {
step("1. Do the first step") {
// ...
captureScreenshot("First step")
}
step("2. Do the second step") {
// ...
captureScreenshot("Second step")
}
}
}
}
There is one parameter passed in the base constructor: - locales - comma-separated string with locales to run test with. Captured screenshots will be available in the device's storage at the path "/sdcard/documents/screenshots/".
For full example, check the ScreenshotSampleTest.
Notice, that the test is marked with @ScreenShooterTest
annotation. This is intended to filter only screenshooter tests to be run. For example, you could pass the
annotation to default AndroidJUnitRunner
with command:
adb shell am instrument -w -e annotation com.kaspersky.kaspresso.annotations.ScreenShooterTest your.package.name/android.support.test.runner.AndroidJUnitRunner
Screenshot files location
All screenshot files are stored in "screenshots" directory by default. They are sorted by locale and test name:
<base directory>/<test class canonical name>/<locale>/<your tag>.png
For the sample test case, the files tree should be like:
- screenshots
- com.kaspersky.kaspressample.tests.docloc.ScreenshotSampleTest
- en
// screenshot files
- ru
// screenshot files
So, in order to save screenshots at external storage, the test application requires
android.permission.WRITE_EXTERNAL_STORAGE
permission.
Screenshot's additional meta-info
When a developer calls captureScreenshot("la-la-la")
method then Kaspresso creates not only a screenshot but also a special xml file. This xml file contains data about all ui elements with their id located on the screen. Example:
<Metadata>
<Window Left="0" Top="0" Width="1440" Height="2560">
<LocalizedString Text="Simple Fragment" LocValueDescription="com.kaspersky.kaspressample.test:id/text_view_title" Top="140" Left="307" Width="825" Height="146"/>
<LocalizedString Text="Button 1" LocValueDescription="com.kaspersky.kaspressample.test:id/button_1" Top="370" Left="84" Width="1272" Height="168"/>
<LocalizedString Text="Button 2" LocValueDescription="com.kaspersky.kaspressample.test:id/button_2" Top="622" Left="84" Width="1272" Height="168"/>
<LocalizedString Text="Kaspresso" LocValueDescription="com.kaspersky.kaspressample.test:id/edit" Top="874" Left="84" Width="1272" Height="158"/>
<LocalizedString Text="Simple screen" LocValueDescription="com.kaspersky.kaspressample.test:id/[id:ffffffff]" Top="51" Left="56" Width="446" Height="93"/>
</Window>
</Metadata>
Screenshots of system dialogs/windows
Sometimes you want to take screenshots of Android system dialogs or windows. That's why you have to change the language for the entire system. For this purpose, there is additional param in DocLocScreenshotTestCase
constructor - changeSystemLocale
. Pay your attention to the fact that changeSystemLocale
defined in true demands Manifest.permission.CHANGE_CONFIGURATION
.
Have a look at the code below:
@RunWith(AndroidJUnit4::class)
class ChangeSysLanguageTestCase : DocLocScreenshotTestCase(
screenshotsDirectory = File("screenshots"),
locales = "en,ru",
changeSystemLocale = true
) {
@ScreenShooterTest
@Test
fun test() {
before{
}.after {
}.run {
step("1. Do the first step") {
// ...
captureScreenshot("First step")
}
step("2. Do the second step") {
// ...
captureScreenshot("Second step")
}
}
}
}
Important note
Please keep the strategy "one docloc test == one screen". If you will seek to capture screenshots from more than one screen during one test consequences may be unpredictable. Be aware.
Advanced usage
In most cases, there is no need to launch certain activity, do a lot of steps before reaching necessary functionality. Often showing fragments will be sufficient to make required screenshots. Also, when you use Model-View-Presenter architectural pattern, you are able to control UI state directly through the View interface. So, there is no need to interact with the application interface and wait for changes.
First create a base test activity with setFragment(Fragment)
method in your application:
class FragmentTestActivity : AppCompatActivity() {
fun setFragment(fragment: Fragment) = with(supportFragmentManager.beginTransaction()) {
replace(android.R.id.content, fragment)
commit()
}
}
Then add a base product screenshot test case:
```kotlin open class ProductDocLocScreenshotTestCase : DocLocScreenshotTestCase( locales = "en,ru" ) {
@get:Rule
val activityTestRule = ActivityTestRule(FragmentTestActivity::class.java, false, true)
protected val activity: FragmentTestActivity
get() = activityTestRule.activity
}
This test case would run your `FragmentTestActivity` on startup. Now you are able to write your screenshooter tests.
For example, create a new test class which extends `ProductDocLocScreenshotTestCase`:
```kotlin
@RunWith(AndroidJUnit4::class)
class AdvancedScreenshotSampleTest : ProductDocLocScreenshotTestCase() {
private lateinit var fragment: FeatureFragment
private lateinit var view: FeatureView
@ScreenShooterTest
@Test
fun test() {
before {
fragment = FeatureFragment()
view = getUiSafeProxy(fragment as FeatureView)
activity.setFragment(fragment)
}.after {
}.run {
step("1. Step 1") {
// ... [view] calls
captureScreenshot("Step 1")
}
step("2. Step 2") {
// ... [view] calls
captureScreenshot("Step 2")
}
step("3. Step 3") {
// ... [view] calls
captureScreenshot("Step 3")
}
// ... other steps
}
}
}
As you might notice, the getUiSafeProxy
method called to get an instance of FeatureView
.
This method wraps your View interface and returns a proxy on it.
The proxy guarantees that all the methods of the View interface you called, will be invoked on the main thread.
There is also getUiSafeProxyFromImplementation
which wraps an implementation rather than an interface.
For full example, check AdvancedScreenshotSampleTest class.
Modifying screenshots path and name
By default, all screenshots are stored at:
/sdcard/documents/screenshots/<locale>/<full qualified test class name>/<method name>.
You can change this behavior by providing custom
ResourcesRootDirsProvider,
ResourcesDirsProvider,
ResourceFileNamesProvider and
ResourcesDirNameProvider implementations.
Find out details here.
Changes
We have been forced to redesign our resource providing system to support Allure.
That's why we changed the primary constructor of DocLocScreenshotTestCase.
But, we've kept the old option of using DocLocScreenshotTestCase
with old resource providing system as a secondary constructor.
You can view the secondary constructor as an example of migration from old system to new system.
Also, we've retained tests using old resource providing system in samples to ensure that nothing is broken.