|
| 1 | +## What is Pixel-by-Pixel Comparison? |
| 2 | + |
| 3 | +This type of test involves the following process: |
| 4 | +1) You write an autotest that brings your application to a certain state and takes a screenshot |
| 5 | +2) The first run of the autotest is performed in screenshot recording mode. The screenshots are saved in your infrastructure. |
| 6 | +3) Each subsequent run of the autotest is performed in a comparison mode. The autotest compares the screenshots from the previous step with the new ones. |
| 7 | + |
| 8 | +If the differences between the screenshots do not exceed the threshold values, the test is considered to be successful. |
| 9 | +If the differences exceed the threshold values, the test fails. In case of a comparison failure, Kaspresso generates an image highlighting |
| 10 | +the differences between the screenshots. |
| 11 | + |
| 12 | +In the example below, the first screenshot is the reference, the second is the new one, and the third is the comparison result. |
| 13 | + |
| 14 | + |
| 15 | +## How to Use Pixel-by-Pixel Comparison |
| 16 | +### Code |
| 17 | +You can see an example of code in [VisualTestSample.kt](https://github.com/KasperskyLab/Kaspresso/blob/master/samples/kaspresso-sample/src/androidTest/kotlin/com/kaspersky/kaspressample/visual/VisualTestSample.kt) |
| 18 | + |
| 19 | +Next, we will break down the key points to pay attention to. |
| 20 | + |
| 21 | +A new autotest should be a subclass of VisualTestCase: |
| 22 | +```kotlin |
| 23 | +class VisualTest : VisualTestCase() { |
| 24 | + // ... |
| 25 | +} |
| 26 | +``` |
| 27 | + |
| 28 | +In the test method, instead of the usual `before{}.after{}.run{}` call, you need to use the `runScreenshotTest`: |
| 29 | +```kotlin |
| 30 | +@Test |
| 31 | +fun test() = runScreenshotTest { |
| 32 | + step("Open Simple Screen") { |
| 33 | + MainScreen { |
| 34 | + simpleButton { |
| 35 | + isVisible() |
| 36 | + click() |
| 37 | + assertScreenshot("some_tag") |
| 38 | + } |
| 39 | + } |
| 40 | + } |
| 41 | +} |
| 42 | +``` |
| 43 | +If you need to perform any actions before or after the test, you can pass the `before` |
| 44 | +and `after` blocks to `runScreenshotTest`: |
| 45 | +```kotlin |
| 46 | +@Test |
| 47 | +fun test() = runScreenshotTest( |
| 48 | + before = {}, |
| 49 | + after = {}, |
| 50 | +) { |
| 51 | + // Test code |
| 52 | +} |
| 53 | +``` |
| 54 | + |
| 55 | +Note the call to `assertScreenshot()`. Ше will save a new screenshot in the recording mode and compare it with the reference in the comparison mode. |
| 56 | + |
| 57 | +### Running |
| 58 | +Pixel-by-pixel comparison tests can work in the two modes - `Record` and `Compare`. |
| 59 | +When you need to record the reference screenshots, use the `Record` mode. |
| 60 | +When the test code is stabilized, run it in `Compare` mode. The line which recorded |
| 61 | +a screenshot in the `Record` mode, now does the comparison with the reference. |
| 62 | + |
| 63 | +There are two ways to choose the test mode: |
| 64 | +1) Manually in the code, by passing a parameter to the VisualTestCase constructor: |
| 65 | +```kotlin |
| 66 | +class VisualTestSample : VisualTestCase( |
| 67 | + kaspressoBuilder = Kaspresso.Builder.simple { |
| 68 | + visualTestParams = VisualTestParams(testType = VisualTestType.Record) |
| 69 | + } |
| 70 | +) |
| 71 | +``` |
| 72 | +2) Set the Gradle property: |
| 73 | +```groovy |
| 74 | +kaspresso.visualTestType="Record" |
| 75 | +``` |
| 76 | + |
| 77 | +**Important** - before running the test, you need to start the adb server. You can find out how to do this [here](Executing_adb_commands.en.md) |
| 78 | + |
| 79 | +After running the test in the recording mode, the screenshots will be saved to the device's memory. |
| 80 | +By default, it will be saved in the `/sdcard/Documents/original_screenshots/` folder. |
| 81 | +Save those screenshots somewhere in your infrastructure. The easiest way is to put them in |
| 82 | +a repository along with the application sources or the same place where you store |
| 83 | +the adb-server jar file. |
| 84 | + |
| 85 | +To make subsequent test runs compare screenshots, you need to switch the test mode |
| 86 | +to `Compare`. If significant differences are found between the screenshots, |
| 87 | +Kaspresso will create a `screenshot_diffs` folder containing auxiliary screenshots |
| 88 | +with differences between the references and new screenshots, and the test will fail. |
| 89 | + |
| 90 | +## How Comparison Works |
| 91 | +Each screenshot is represented as a two-dimensional array. Each value in the array is |
| 92 | +the color of a pixel. The test goes through two arrays (reference and new), |
| 93 | +compares individual pixels and calculates the ratio of the number of the |
| 94 | +pixels that do differ to the total number of pixels. |
| 95 | + |
| 96 | +This type of test should be run on the same device configuration (preferably on the same AVD), |
| 97 | +but even in this case, the same test can generate the screenshots that differ from each |
| 98 | +other by several pixels. This is mainly due to the peculiarities of rendering on |
| 99 | +different hardware. |
| 100 | + |
| 101 | +To solve this problem, you can set a threshold value for comparison. If the difference |
| 102 | +between the reference and the new screenshot is less than the specified value, the |
| 103 | +test will be considered successful. The default threshold value is 0.3%. |
| 104 | + |
| 105 | +The threshold value is set by the `tolerance` field in the `VisualTestParams` class. |
| 106 | + |
| 107 | +To determine if an individual pixel differs, a comparison of each of the RGB channels of |
| 108 | +the reference and the original is performed. If the difference between at least one of the |
| 109 | +channels is greater than the value in the `colorTolerance` field of the `VisualTestParams` |
| 110 | +class, the pixel is considered different. Note that the value in the channel ranges from |
| 111 | +0 to 255. |
| 112 | + |
| 113 | +## Test Configuration |
| 114 | +For more fine-grained configuration of screenshot comparison, the `VisualTestParams` class |
| 115 | +is used. You need to set it up in the Kaspresso builder: |
| 116 | +```kotlin |
| 117 | +class VisualTestSample : VisualTestCase(kaspressoBuilder = Kaspresso.Builder.simple { |
| 118 | + visualTestParams = VisualTestParams( |
| 119 | + testType = VisualTestType.Compare, |
| 120 | + hostScreenshotsDir = "original_screenshots", |
| 121 | + colorTolerance = 1, |
| 122 | + tolerance = 0.3f |
| 123 | + ) |
| 124 | +}) { |
| 125 | + // ... |
| 126 | +} |
| 127 | +``` |
| 128 | +You can read more about what each parameter is responsible for in the Javadoc. |
| 129 | + |
| 130 | +## Allure Support |
| 131 | +If you use Allure to generate reports and want to configure pixel-by-pixel comparison tests, |
| 132 | +you need to make the test class a subclass of `AllureVisualTestCase`: |
| 133 | + |
| 134 | +```kotlin |
| 135 | +class VisualTest : AllureVisualTestCase() { |
| 136 | + // ... |
| 137 | +} |
| 138 | +``` |
| 139 | +A step with a failed comparison will be marked in the report. To make the test fail on |
| 140 | +the first failed comparison, you need to pass the `failEarly` parameter with a value |
| 141 | +of `true` to the `AllureVisualTestCase` constructor: |
| 142 | +```kotlin |
| 143 | +class VisualTest : AllureVisualTestCase(failEarly = true) { |
| 144 | + // ... |
| 145 | +} |
| 146 | +``` |
0 commit comments