Storing resources for unit and espresso tests - android

Currently, I'm storing the same resources in
/test/resources/sample.txt //for unit tests
/main/asssets/sample.txt //for espresso tests
can I store them in one place?
If yes, how can I read them in my tests?
Preferably, I would like to open them as the File object, not InputStream.

So, I finally decided that the best solution, in this case, is to store everything in assets folder (in mock flavor).
You can open them from instrumentation test:
activityRule.activity.assets.open("sample.txt")
from unit tests:
private val ASSET_BASE_PATH = "../app/src/mock/assets/"
fun openAsset(filename: String): InputStream {
return FileInputStream(ASSET_BASE_PATH + filename)

Related

How to conditionally skip a unit test in Android and kotlin?

I need to run a unit test based on whether this asset exists at runtime. The reason is, I am downloading the file in react native and in android, I am running some unit tests that requires this file.
I would like to run the unit test only if this file exists. Does anyone have any suggestions or code samples on how this can be achieved? Or is there another way these unit tests can be accomplished?
You shouldn't do that in a unit test because you want to have the file locally in your testing environment or have a mock that provides it. Otherwise you're not Eradicating Non-Determinism in Tests.
Let's assume you need to do something like that anyway. So, I would add a condition in the assert expression:
#Test
fun `Given a variable When has a value Then assert it has a value`(){
var myVar = null
myVar = getAValue()
myVar?.let {
assertNotEquals(null, myVar)
}
}
To me, eradicating non determinism in this particular context means that test scope has always specific expected values, the conditional expression in the let or an if enclosing an assert expression violates that. Hence the code above shouldn't be part of your tests. Instead, you have to write a test for the case myVar is null, you write another test for the case myVar is non null.
That's why I use Given, When, Then the conditional state would make the Given/When very messy.

Unit test a helper class around SharedPreference

I have a helper class to save user object to shared preferences. I have used a serialize(): String function and a create(serializedString: String) function in my User data model. They use GSon serializer and are working good as suggested by the unit tests on them.
Now my helper class is called SharedPreferenceUserStore.kt which takes a Context object. The code is:
class SharedPreferenceUserStore(context: Context) {
companion object {
val TAG = SharedPreferenceUserStore::class.java.simpleName
}
var userLocalSharedPref: SharedPreferences =
context.getSharedPreferences(USER_LOCAL_STORE_SHARED_PREF_NAME, Context.MODE_PRIVATE)
/*
Store the required data to shared preference
*/
#SuppressLint("ApplySharedPref")
fun storeUserData(user: User) {
val userLocalDatabaseEditor = userLocalSharedPref.edit()
val serializedData = user.serialize()
userLocalDatabaseEditor.putString(
USER_LOCAL_STORE_SHARED_PREF_SERIALIZED_DATA_KEY,
serializedData
)
if (userLocalDatabaseEditor.commit()) {
Log.d(TAG, " Store Commit return true")
}
}
/*
Clear all the locally stored data from the shared pref
*/
#SuppressLint("ApplySharedPref")
fun clearUserData() {
val userLocalDatabaseEditor = userLocalSharedPref.edit()
userLocalDatabaseEditor.clear()
userLocalDatabaseEditor.commit()
}
fun getLoggedInUser(): User? {
val stringUser = userLocalSharedPref.getString(
USER_LOCAL_STORE_SHARED_PREF_SERIALIZED_DATA_KEY, "")
return if (stringUser==null || stringUser == ""){
null
} else{
User.create(stringUser)
}
}
And I have written some unit tests for this helper class as follows:
#RunWith(JUnit4::class)
class SharedPreferenceUserStoreTest {
lateinit var sharedPreferenceUserStore: SharedPreferenceUserStore
lateinit var user: User
//to be mocked
lateinit var sharedPreferences: SharedPreferences
lateinit var sharedPreferencesEditor: SharedPreferences.Editor
lateinit var context: Context
#Before
fun setUp() {
//mocking Context and SharedPreferences class
context = mock(Context::class.java)
sharedPreferences = mock(SharedPreferences::class.java)
sharedPreferencesEditor = mock(SharedPreferences.Editor::class.java)
//specifying that the context.getSharedPreferences() method call should return the mocked sharedpref
`when`<SharedPreferences>(context.getSharedPreferences(anyString(), anyInt()))
.thenReturn(sharedPreferences)
//specifying that the sharedPreferences.edit() method call should return the mocked sharedpref editor
`when`(sharedPreferences.edit()).thenReturn(sharedPreferencesEditor)
//specifying that the sharedPreferencesEditor.putString() method call should return the mocked sharedpref Editor
`when`(sharedPreferencesEditor.putString(anyString(), anyString())).thenReturn(
sharedPreferencesEditor
)
`when`(sharedPreferences.getString(anyString(), anyString())).thenReturn("")
//instantiating SharedPreferenceUserStore from the mocked context
sharedPreferenceUserStore = SharedPreferenceUserStore(context)
user = User(
35,
"Prashanna Bhandary",
"prashanna.bhandary#gmail.com",
"dd58a617ea618010c2052cb54079ad67.jpeg",
"98********",
"test address 01",
1,
"yes",
"2019-08-30 04:56:43",
"2019-08-30 05:14:47",
0
)
}
#After
fun tearDown() {
}
#Test
fun passUser_storeUserData() {
sharedPreferenceUserStore.storeUserData(user)
verify(sharedPreferencesEditor).putString(
Constants.USER_LOCAL_STORE_SHARED_PREF_SERIALIZED_DATA_KEY,
user.serialize()
)
verify(sharedPreferencesEditor).commit()
}
#Test
fun testClearUserData() {
sharedPreferenceUserStore.clearUserData()
verify(sharedPreferencesEditor).clear()
}
#Test
fun testGetLoggedInUser_storeNotCalled() {
//calling getLoggedInUser() without calling storeUserData() should give null
assertEquals(null, sharedPreferenceUserStore.getLoggedInUser())
//verify that getString() was called on the shared preferences
verify(sharedPreferences).getString(Constants.USER_LOCAL_STORE_SHARED_PREF_SERIALIZED_DATA_KEY, "")
}
#Test
fun testGetLoggedInUser_storeCalled(){
//call getLoggedInUser(), we are expecting null
assertNull(sharedPreferenceUserStore.getLoggedInUser())
//verify that getString() was called on the shared preferences
verify(sharedPreferences).getString(Constants.USER_LOCAL_STORE_SHARED_PREF_SERIALIZED_DATA_KEY, "")
}
}
As I am really new to Unit Testing and Mocking libraries like Mockito. Now my question is are my tests any good? and I wanted to test if the getLoggedInUser() funciton of my helper class is doing what it is supposed to do (ie. get logged in user if shared pref has it), how do I do that?
In addition do suggest me any improvements I can make to my test or the helper class itself. Thank you.
Judging your test for what it is - A unit test running on a host machine with Android dependencies mocked with Mockito - it looks fine and like what you would expect.
The benefit-to-effort ratio of such tests are debatable, though. Personally I think it would be more valuable to run such a test against the real SharedPreferences implementation on a device, and assert on actual side effects instead of verifying on mocks. This has a couple of benefits over mocked tests:
You don't have to re-implement SharedPreferences with mocking
You know that SharedPreferenceUserStore will work with the real SharedPreferences implementation
But, such tests also have a debatable benefit-to-effort ratio. For a solo developer project, think about what kind of testing that is most important. Your time is limited so you will only have time to spend on writing the most important kind of tests.
The most important kinds of tests are the ones that test your app in the same way your users will use it. In other words, write high-level UI Automator tests. You can write how many mocked or on-device unit tests as you want. If you don't test that your entire app as a whole works, you will not know that it works. And if you don't know that your app as a whole works, you can't ship it. So in some way you have to test your app in its entirety. Doing it manually quickly becomes very labour intensive as you add more and more functionality. The only way to continually test your app is to automate the high-level UI testing of your app. That way you will also get code coverage that matters.
One big benefit of high-level UI testing that is worth pointing out is that you don't have to change them whenever you change some implementation detail in your app. If you have lots of mocked unit tests, you will have to spend a lot of time to refactor your unit tests as you refactor the real app code, which can be very time consuming, and thus a bad idea if you are a solo developer. Your UI Automator tests do not depend on low-level implementation details and will thus remain the same even if you change implementation details.
For example, maybe in the future you want to use Room from Android Jetpack to store your user data instead of SharedPreference. You will be able to do that without changing your high level UI tests at all. And they will be a great way to regression test such a change. If all you have are mocked unit tests, it will be a lot of work to rewrite all relevant unit tests to work with Room instead.
I agree with what #Enselic say about favoring Integration Test over Unit Tests.
However I disagree with his statement that this mockito test looks fine.
The reason for that is that (almost) every line in your code under test involves a mock operation. Basically mocking the complete method would have the same result.
What you are doing in your test is testing that mockito works as expected, which is something you should not need to test.
On the other hand your test is a complete mirror of the implementation itself. Which means everytime you refactor something, you have to touch the test. Preferably would be a black box test.
If you use Mockito you should try to restrict its use to methods that actually do something (that is not mocked).
Classes that generally should be mocked for testing purposes are dependencies that interact with external components (like a database or a webservice), however in these cases you are normally required to have Integration Tests as well.
And if your Integration-Tests already cover most part of the code, you can check whether you want to add a test using a mock for those parts that are not covered.
I have no official source for what I am trying to express, its just based on my experience (and therefore my own opinion). Treat it as such.
There is not much that can be said regarding the tests that guys before me haven't said.
However, one thing that you might want to consider is refactoring your SharedPreferenceUserStore to accept not Context(which is quite a huge thing, and if not handled properly could lead to unforeseen issues and/or memory leaks), but rather SharedPreferences themselves. This way, your class, that deals only with updating the prefs doesn't have access to more than it should.

Integrating Robolectric and Cucumber

I want to combine both Robolectric and Cucumber (JVM).
Currently I have two classes ActivityStepdefs where two step definitions for activity management are defined.
My second class is RoActivity Where for example an activity is created from it's class name, and where Robolectric will be used.
When I run RoActivityTest using RobolectricTestRunner the test in this class passes, but when I run RunCukesTest (class for running features as junit test) the code from RoActivity is not running as part of Robolectric, i.e. RunCukesTest search for features on my project and match it with a method inside ActivityStepdefs and finally this class will call a method from RoActivity
Is possible to run test with both junit both* runners?
I'm not sure but perhaps it's possible to do something like powermock, using junit rules.
In that case for which one should I have to define the rule?
*Cucumber and Robolectric
My small 5 cents.
Cucumber is mostly used for acceptance tests (correct me if you use it for unit testing) and Robolectric is mostly used for unit testing.
As for me, it is overkill to write cucumber during TDD. And Robolectric is still not android and I would run acceptance tests on real device or at least emulator.
I'am facing the same problem, after some google work, I got a solution:
#RunWith(ParameterizedRobolectricTestRunner::class)
#CucumberOptions( features = ["src/test/features/test.feature","src/test/features/others.feature"], plugin = ["pretty"])
class RunFeatures(val index: Int, val name:String) {
companion object {
#Parameters(name = "{1}")
#JvmStatic
fun features(): Collection<Array<Any>> {
val runner = Cucumber(RunFeatures::class.java)
Cucumber()
val children = runner.children
return children.mapIndexed{index, feature ->
arrayOf(index,feature.name)
}
}
}
#Test
fun runTest() {
val core = JUnitCore()
val feature = Cucumber(RunFeatures::class.java).children[index]!!
core.addListener(object: RunListener() {
override fun testFailure(failure: Failure?) {
super.testFailure(failure)
fail("$name failed:\n"+failure?.exception)
}
})
val runner = Request.runner(feature)
core.run(runner)
}
}
but seems not an pretty solution for me, can somebody help me out these problem:
must explicitly list all feature file path. but cannot use pattern such as *.feature
when failed cannot know which step failed.
parameter can only pass primitive type data,
I've get into cucumber source , but seems CucumberOptions inline Cucumber , I cannot pass it programmatically but can only use annotation .

android test fixtures

I am working on an android app and I have created a new test project for unit tests. Where's recommended to store the test fixtures (like xml files and the like) and what's the proper way to access it ?
It depends if you really mean unit test or instrumented tests (like using espresso and stuff)...
Unit tests:
Put your fixtures in src/test/resources (so e.g. src/test/resources/fixture.json)
Access them from your test classes e.g. using:
InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("fixture.json")
Instrumented tests:
Put your fixtures in src/androidTest/assets/ (so e.g. src/androidTest/assets/fixture.json)
Access them from your test classes e.g. using:
InputStream is = InstrumentationRegistry.getContext().getResources().getAssets().open("fixture.json")
Here are some examples of how you can convert InputStream to String.
Here's a pretty good post describing different cases.
P.S. I know this question is 6+ years old... answering for any future searches.
After some searching I found there is no one proper way to store fixtures in Android (also not so much in java either).
Fixtures are a way to provide easy and consistent data to be passed into test cases. Creating .java classes with static methods that return objects is one way to go.
Example:
public class Pojos {
public static List<TaskListItem> taskListItems() {
return Arrays.asList(
new TaskListItem("one"),
new TaskListItem("two"),
new TaskListItem("three")
);
}
}
You can configure Gradle to read resources from a shared folder, you will be able to share code and resources either in unit test or instrumented test by doing the following.
android {
// ...
sourceSets {
final String sharedJavaDir = 'src/sharedTest/java'
final String sharedResourcesDir = 'src/sharedTest/resources'
test.java.srcDirs += sharedJavaDir
test.resources.srcDirs += [sharedResourcesDir]
androidTest.java.srcDirs += sharedJavaDir
androidTest.resources.srcDirs += [sharedResourcesDir]
// ....
}
// ...
}
Let's imagine that I setup a shared test resource in
/app/src/sharedTest/resources/test_fixture_file.txt
Kotlin code
In a UnitTest or Instrumented test you can use.
val resource: InputStream? = this.javaClass.getResourceAsStream("/test_fixture_file.txt")

Android: How to test a custom view?

There are several methods of unit testing in Android, what's the best one for testing a custom view I've written?
I'm currently testing it as part of my activity in an instrumentation test case, but I'd rather test just the view, isolated.
A simple solution for the lack of a View-focused TestCase implementation would be to create a simple Activity within your test project that includes your view. This will allow you to write tests against the view using a simple Activity. Information on Activity testing:
http://developer.android.com/reference/android/test/ActivityUnitTestCase.html
As mentioned in wikibooks:
unit testing is a method by which individual units of source code are tested to determine if they are fit for use.
So when you say you want to test your custom view, you can check various methods of your custom views like "onTouchEvent", "onDown", "onFling", "onLongPress", "onScroll", "onShowPress", "onSingleTapUp", "onDraw" and various others depending on your business logic. You can provide mock values and test it. I would suggest two methods of testing your custom view.
1) Monkey Testing
Monkey testing is random testing performed by automated testing tools.
G.D.S. Prasad on geekinterview.com
and:
A monkey test is a unit test that runs with no specific test in mind. The monkey in this case is the producer of any input. For example, a monkey test can enter random strings into text boxes to ensure handling of all possible user input or provide garbage files to check for loading routines that have blind faith in their data.
sridharrganesan on geekinterview.com
This is a black box testing technique and it can check your custom view in so many unique conditions that you will get astonished :) .
2) Unit Testing
2a) Use Robotium Unit Testing Framwork
Go to Robotium.org or http://code.google.com/p/robotium/ and download the example test project. Robotium is a really easy to use framework that makes testing of android applications easy and fast. I created it to make testing of advanced android applications possible with minimum effort. Its used in conjunction with ActivityInstrumentationTestCase2.
2b) Use Android Testing Framework
Here are the links to the reference:
http://developer.android.com/reference/android/test/ActivityInstrumentationTestCase2.html
and
http://developer.android.com/reference/android/test/ActivityUnitTestCase.html
For starters:
http://developer.android.com/guide/topics/testing/testing_android.html
According to one user : Aside from easily testing non platform
dependent logic I haven't found a
clever way to run tests, so far (at
least for me) any actual platform
logic testing is cumbersome. It's
almost non trivial anyway because I've
found differences in implementation
between the emulator and my actual
device and I hate to run a unit test
implementation on my device just to
remove the application afterwards.
My strategy has been: Try to be
concise and make the logic well
thought out and then test
implementation piece by piece (less
then desirable).
Also Stephen Ng provides good aproach for real Unit Test for Android projects solution: https://sites.google.com/site/androiddevtesting/
One user has made a screencast.
Here's a ScreenCast I made on how I got Unit Tests to work. Simple Unit
Tests and more complex unit tests that
depend on having a reference to
Context or Activity objects.
http://www.gubatron.com/blog/2010/05/02/how-to-do-unit-testing-on-android-with-eclipse/
Hope it helps you testing your custom view in all possible conditions :)
Comment (futlib) All your suggestions seem to involve testing the ACTIVITY, while I really want to test just the VIEW. I might want to use this view in other activities, so it doesn't make much sense for me to test it with a specific one. – futlib
Answer: To implement a custom view,
you will usually begin by providing
overrides for some of the standard
methods that the framework calls on
all views. For example "onDraw",
"onKeyDown(int, KeyEvent)",
"onKeyUp(int, KeyEvent)",
"onTrackballEvent(MotionEvent)" etc of
your custom view. So when you want to
do unit testing for your custom you'll
have to test these methods, and
provide mock values to it so that you
can test your custom view on all
possible cases. Testing these methods
doesn't mean that you are testing your
ACTIVITY, but it means testing your
custom view (methods/functions) which
is within an activity. Also you'll
have to put your custom view in an
Activity eventually for your target
users to experience it. Once
thoroughly tested , your custom view
can be placed in many projects and
many activities.
Here's a different suggestion which works fine in many cases: Assuming you are referencing your custom view from within a layout file, you can use an AndroidTestCase, inflate the view, and then perform tests against it in isolation. Here's some example code:
my_custom_layout.xml:
<?xml version="1.0" encoding="utf-8"?>
<de.mypackage.MyCustomView ...
MyCustomView.java:
public class MyCustomView extends LinearLayout {
public MyCustomView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void setTitle(CharSequence title) {
((TextView) findViewById(R.id.mylayout_title_textView)).setText(title);
}
...
MyCustomViewTest.java:
public class MyCustomViewTest extends AndroidTestCase {
private MyCustomView customView;
#SuppressLint("InflateParams")
#Override
protected void setUp() throws Exception {
super.setUp();
customView = (MyCustomView) LayoutInflater.from(getContext())
.inflate(R.layout.my_custom_layout, null);
}
public void testSetTitle_SomeValue_TextViewHasValue() {
customView.setTitle("Some value");
TextView titleTextView = (TextView) valueSelection.findViewById(R.id.mylayout_title_textView);
assertEquals("Some value", titleTextView.getText().toString());
}
...
I struggled a lot to set up screenshot tests for my custom view.
Here is how I managed to do that and everything I learned in the process.
It may not be the most convenient method, but I put it here anyway.
And of course, screenshot testing is now a little bit easier in Jetpack Compose.
⚠ Caution #1
You can use JUnit 4 if you want. I'm using JUnit 5. Because JUnit 5 is built on Java 8 from the ground up, its instrumentation tests will only run on devices running Android 8.0 (API 26) or newer. Older phones/emulators will skip the execution of these tests completely, marking them as ignored.
If you want to run JUnit 5 tests on Android, refer to this answer for how to set it up.
⚠ Caution #2
The screenshot tests may not work on other devices even if they have the same screen DPI (they may not work at all on devices with different screen DPIs). For example, even when I use the same device in my local machine and on GitHub Actions to run the tests, they do not produce the same result (GitHub Actions assertions fail). So, I had to disable them on GitHub Actions.
If you want to disable the screenshot tests on GitHub Actions (or other CI), see this answer.
⚠ Caution #3
If you have resources in instrumented tests (in androidTest source set) and you want to reference their id, you should use them like this (note the package name followed by .test):
com.example.test.R.id.an_id
For example, if your package name is my.package.name then to access the layout file in src/androidTest/res/layout/my_layout.xml in your tests, you use my.package.name.test.R.layout.my_layout.
⚠ Caution #4
Since we are saving our test screenshots on the external storage of the device/emulator, we need to make sure that we have both WRITE_EXTERNAL_STORAGE permission added in the manifest and adb install options -g and -r configured in the build script. When running on Marshmallow+, we also need to have those permissions granted before running a test. -g is for granting permissions when installing the app (works on Marshmallow+ only) while -r is to allow reinstalling of the app.
These correspond to adb shell pm install options.
Just be aware that this does not work with Android Studio yet.
So, create an AndroidManifest.xml file in src/androidTest/ directory and add the following to it:
<manifest package="my.package.name">
<!-- For saving screenshots in tests -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
tools:ignore="ScopedStorage"
tools:remove="android:maxSdkVersion"/>
<application android:requestLegacyExternalStorage="true">
<activity android:name=".MyActivityThatContainsTheView"/>
</application>
</manifest>
and add the adb install options in your library Gradle build file:
android {
// Note that adbOptions block is deprecated in Android Gradle Plugin 7.0.0;
// replace adbOptions block with installation block
adbOptions {
installOptions("-g", "-r")
}
}
⚠ Caution #5
I save the reference screenshot (the one I want to compare with the current screenshot) in src/androidTest/assets directory. So, specify that directory as an assets entry in the library build file:
android {
sourceSets {
// This is Kotlin DSL; see https://stackoverflow.com/a/59920318 for groovy DSL
get("debug").assets.srcDirs("src/androidTest/assets")
}
⚠ Caution #6
To pass instrumentation arguments when running the tests (like shouldSave in my code), do this:
For a Gradle task:
Running the task from command line: pass your arguments after the task name
./gradlew myTask -Pandroid.testInstrumentationRunnerArguments.shouldSave=true
Running the task with Studio: pass your arguments in run config Arguments: field
-Pandroid.testInstrumentationRunnerArguments.shouldSave=true
For an Android Studio Android Instrumented Tests run configuration:
Select Edit Configurations... from run configuration popup, then select your run configuration, click ... in front of Instrumentation arguments: field and then add a name-value entry like Name shouldSave Value true.
See this article and this post.
⚠ Caution #7
The first time you want to run the screenshot tests and also whenever you update your custom view that might change its visuals, you should run the tests passing true for shouldSave argument so the new screenshots are saved in the device (see comments above save method in code below for the location of the images) and then manually copy the new screenshots to your src/androidTest/assets/ directory so they will be the new reference ones.
⚠ Caution #8
Make sure to use -ktx versions of androidx libraries (like AndroidX Core library) for Kotlin.
The -ktx variants contain useful Kotlin extension functions. Example:
implementation("androidx.core:core-ktx:1.6.0")
⚠ Caution #9
Make sure the device screen is on and unlocked for the activity to go to resumed state.
The code
This is my test activity in src/androidTest/java/com/example/ directory that exposes the view that I want to take its screenshot as a property:
class MyActivityThatContainsTheView : AppCompatActivity() {
lateinit var myView: MyView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(my.package.name.test.R.layout.my_layout_that_contains_the_view)
myView = findViewById(my.package.name.test.R.id.my_view_id_in_the_layout_file)
}
}
And finally, this is my tests and how I save, load, and compare the screenshots:
#DisabledIfBuildConfigValue(named = "CI", matches = "true")
class ScreenshotTestView {
#JvmField
#RegisterExtension
val scenarioExtension = ActivityScenarioExtension.launch<MyActivityThatContainsTheView>()
lateinit var scenario: ActivityScenario<MyActivityThatContainsTheView>
// See ⚠ Caution #6 above in the post
val shouldSave = InstrumentationRegistry.getArguments().getString("shouldSave", "false").toBoolean()
val shouldAssert = InstrumentationRegistry.getArguments().getString("shouldAssert", "true").toBoolean()
#BeforeEach fun setUp() {
scenario = scenarioExtension.scenario
scenario.moveToState(Lifecycle.State.RESUMED)
}
#Test fun test1() {
val screenshotName = "screenshot-1"
scenario.onActivity { activity ->
val view = activity.myView
view.drawToBitmap()
.saveIfNeeded(shouldSave, screenshotName)
.assertIfNeeded(shouldAssert, screenshotName)
}
}
fun Bitmap.saveIfNeeded(shouldSave: Boolean, name: String): Bitmap {
if (shouldSave) save(name)
return this
}
fun Bitmap.assertIfNeeded(shouldCompare: Boolean, screenshotName: String) {
if (shouldCompare) assert(screenshotName)
}
/**
* The screenshots are saved in /Android/data/my.package.name.test/files/Pictures
* on the external storage of the device.
*/
private fun Bitmap.save(name: String) {
val context = InstrumentationRegistry.getInstrumentation().targetContext
val path = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES)
val file = File(path, "$name.png")
file.outputStream().use { stream ->
compress(Bitmap.CompressFormat.PNG, 100, stream)
}
}
private fun Bitmap.assert(screenshotName: String) {
val reference = loadReferenceScreenshot(screenshotName)
// I'm using AssertJ library; you can simply use assertTrue(this.sameAs(reference))
assertThat(this.sameAs(reference))
.withFailMessage { "Screenshots are not the same: $screenshotName.png" }
.isTrue()
}
private fun loadReferenceScreenshot(name: String): Bitmap {
val context = InstrumentationRegistry.getInstrumentation().context
val assets = context.resources.assets
val reference = assets.open("$name.png").use { stream ->
BitmapFactory.decodeStream(stream)
}
return reference
}
}

Categories

Resources