Skip to content

feat(Android): #8011 Convert disabled Android Unit Tests to Instrumentation Tests #8041

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions lib/android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ android {
defaultConfig {
minSdkVersion safeExtGetFallbackLowerBound('minSdkVersion', DEFAULT_MIN_SDK_VERSION)
targetSdkVersion safeExtGetFallbackLowerBound('targetSdkVersion', DEFAULT_TARGET_SDK_VERSION)

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
Expand All @@ -53,6 +55,15 @@ android {
}

testOptions {
managedDevices {
localDevices {
pixel3aapi34 {
device = "Pixel 3a"
apiLevel = 34
systemImageSource = "aosp-atd"
}
}
}
unitTests.includeAndroidResources = true
unitTests.all { t ->
maxHeapSize = "4g"
Expand Down Expand Up @@ -102,6 +113,7 @@ dependencies {
//noinspection GradleDynamicVersion
implementation 'com.facebook.react:react-native:+'


if ("Playground".toLowerCase() == rootProject.name.toLowerCase()) {
// tests only for our playground
testImplementation 'junit:junit:4.13.2'
Expand All @@ -112,5 +124,22 @@ dependencies {
testImplementation 'org.mockito:mockito-inline:4.6.1'
testImplementation "org.mockito.kotlin:mockito-kotlin:4.0.0"
testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlinVersion"

// Core testing libraries
androidTestImplementation "androidx.test.ext:junit:1.2.1"
androidTestImplementation 'org.assertj:assertj-core:3.11.1'

androidTestImplementation "androidx.test.espresso:espresso-core:3.6.1"

androidTestImplementation "com.linkedin.dexmaker:dexmaker-mockito-inline:2.28.4"
androidTestImplementation 'org.opentest4j:opentest4j:1.2.0'
androidTestImplementation ("org.mockito.kotlin:mockito-kotlin:5.4.0") {
exclude group: 'org.mockito', module: 'mockito-core'
}

androidTestImplementation "androidx.test.uiautomator:uiautomator:2.3.0"

androidTestImplementation("com.facebook.react:hermes-android")

}
}
17 changes: 17 additions & 0 deletions lib/android/app/src/androidTest/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.reactnativenavigation.test">

<application
android:name="com.reactnativenavigation.TestApplication">
<activity
android:name="com.reactnativenavigation.TestActivity"
android:exported="true"
android:theme="@style/Theme.AppCompat.Light.NoActionBar"
>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.reactnativenavigation

abstract class BaseAndroidTest {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.reactnativenavigation

class TestActivity : NavigationActivity() {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.reactnativenavigation

import com.facebook.react.ReactHost
import com.facebook.react.ReactNativeHost
import com.facebook.react.ReactPackage
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load
import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost
import com.facebook.react.shell.MainReactPackage
import com.reactnativenavigation.react.NavigationPackage
import com.reactnativenavigation.react.NavigationReactNativeHost

class TestApplication : NavigationApplication() {


override val reactNativeHost: ReactNativeHost
get() = object : NavigationReactNativeHost(this) {
override fun getJSMainModuleName(): String {
return "index"
}

override fun getUseDeveloperSupport(): Boolean {
return false
}

public override fun getPackages(): List<ReactPackage> {
val packages = listOf(MainReactPackage(null), NavigationPackage())
return packages
}

override val isHermesEnabled: Boolean
get() = true

override val isNewArchEnabled: Boolean
get() = true
}

override fun onCreate() {
super.onCreate()
load()
}

override val reactHost: ReactHost
get() = getDefaultReactHost(this, reactNativeHost)

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package com.reactnativenavigation;

import android.app.Activity;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;

import androidx.annotation.NonNull;

import com.reactnativenavigation.mocks.TitleBarReactViewCreatorMock;
import com.reactnativenavigation.mocks.TopBarBackgroundViewCreatorMock;
import com.reactnativenavigation.mocks.TitleBarButtonCreatorMock;
import com.reactnativenavigation.mocks.TypefaceLoaderMock;
import com.reactnativenavigation.options.Options;
import com.reactnativenavigation.options.params.Bool;
import com.reactnativenavigation.utils.RenderChecker;
import com.reactnativenavigation.viewcontrollers.stack.StackPresenter;
import com.reactnativenavigation.react.events.EventEmitter;
import com.reactnativenavigation.utils.CompatUtils;
import com.reactnativenavigation.utils.ImageLoader;
import com.reactnativenavigation.utils.UiUtils;
import com.reactnativenavigation.viewcontrollers.child.ChildControllersRegistry;
import com.reactnativenavigation.viewcontrollers.viewcontroller.ViewController;
import com.reactnativenavigation.viewcontrollers.stack.topbar.button.IconResolver;
import com.reactnativenavigation.viewcontrollers.stack.StackControllerBuilder;
import com.reactnativenavigation.viewcontrollers.stack.topbar.TopBarController;
import com.reactnativenavigation.views.stack.StackLayout;
import com.reactnativenavigation.views.stack.topbar.TopBar;

import org.mockito.Mockito;

public class TestUtils {
public static StackControllerBuilder newStackController(Activity activity) {
TopBarController topBarController = new TopBarController() {
@Override
protected TopBar createTopBar(@NonNull Context context, @NonNull StackLayout stackLayout) {
TopBar topBar = super.createTopBar(context, stackLayout);
topBar.layout(0, 0, 1000, UiUtils.getTopBarHeight(context));
return topBar;
}
};
return new StackControllerBuilder(activity, Mockito.mock(EventEmitter.class))
.setId("stack" + CompatUtils.generateViewId())
.setChildRegistry(new ChildControllersRegistry())
.setTopBarController(topBarController)
.setStackPresenter(new StackPresenter(activity, new TitleBarReactViewCreatorMock(),
new TopBarBackgroundViewCreatorMock(), new TitleBarButtonCreatorMock(),
new IconResolver(activity, new ImageLoader()), new TypefaceLoaderMock(), new RenderChecker(),
new Options()))
.setInitialOptions(new Options());
}

public static void hideBackButton(ViewController<?> viewController) {
viewController.options.topBar.buttons.back.visible = new Bool(false);
}

public static <T extends View> T spyOn(T child) {
ViewGroup parent = (ViewGroup) child.getParent();
int indexOf = parent.indexOfChild(child);
parent.removeView(child);
T spy = Mockito.spy(child);
parent.addView(spy, indexOf);
return spy;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.reactnativenavigation.fakes

import android.app.Activity
import com.reactnativenavigation.mocks.ImageLoaderMock
import com.reactnativenavigation.utils.ImageLoader
import com.reactnativenavigation.viewcontrollers.stack.topbar.button.IconResolver

class IconResolverFake(activity: Activity, imageLoader: ImageLoader = ImageLoaderMock.mock()) : IconResolver(activity, imageLoader)
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.reactnativenavigation.mocks;

import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.drawable.Drawable;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

public class BackDrawable extends Drawable {
@Override
public void draw(@NonNull Canvas canvas) {

}

@Override
public void setAlpha(int alpha) {

}

@Override
public void setColorFilter(@Nullable ColorFilter colorFilter) {

}

@Override
public int getOpacity() {
return 0;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.reactnativenavigation.mocks

import android.graphics.Canvas
import android.graphics.ColorFilter
import android.graphics.drawable.Drawable

import com.reactnativenavigation.utils.ImageLoader
import com.reactnativenavigation.utils.ImageLoader.ImagesLoadingListener
import org.mockito.Mockito.doAnswer
import org.mockito.kotlin.any
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
import java.util.*

object ImageLoaderMock {
val mockDrawable: Drawable = object : Drawable() {
override fun draw(canvas: Canvas) {}
override fun setAlpha(alpha: Int) {}
override fun setColorFilter(colorFilter: ColorFilter?) {}
override fun getOpacity(): Int {
return 0
}
}
private val backIcon: Drawable = BackDrawable()

@JvmStatic
fun mock(): ImageLoader {
return this.mock(mockDrawable)
}

@JvmStatic
fun mock(returnDrawable: Drawable = mockDrawable): ImageLoader {
val imageLoader = mock<ImageLoader>()
doAnswer { invocation ->
val urlCount = (invocation.arguments[1] as Collection<*>).size
val drawables = Collections.nCopies(urlCount, returnDrawable)
(invocation.arguments[2] as ImagesLoadingListener).onComplete(drawables)
null
}.`when`(imageLoader).loadIcons(any(), any(), any())

doAnswer { invocation ->
(invocation.arguments[2] as ImagesLoadingListener).onComplete(returnDrawable)
null
}.`when`(imageLoader).loadIcon(any(), any(), any())

whenever(imageLoader.getBackButtonIcon(any())).thenReturn(backIcon)
return imageLoader
}
}
Loading