Skip to content

Commit ef49f56

Browse files
Add Android unit tests to plugin template (#120720)
* Add Java tests * Add Kotlin * Add integration testing * Add cerate tests
1 parent a664f08 commit ef49f56

7 files changed

Lines changed: 186 additions & 24 deletions

File tree

dev/devicelab/lib/tasks/plugin_tests.dart

Lines changed: 38 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -252,36 +252,50 @@ public class $pluginClass: NSObject, FlutterPlugin {
252252
// build files.
253253
await build(buildTarget, validateNativeBuildProject: false);
254254

255-
if (buildTarget == 'ios') {
256-
await testWithNewIOSSimulator('TestNativeUnitTests', (String deviceId) async {
255+
switch(buildTarget) {
256+
case 'apk':
257+
if (await exec(
258+
path.join('.', 'gradlew'),
259+
<String>['testDebugUnitTest'],
260+
workingDirectory: path.join(rootPath, 'android'),
261+
canFail: true,
262+
) != 0) {
263+
throw TaskResult.failure('Platform unit tests failed');
264+
}
265+
break;
266+
case 'ios':
267+
await testWithNewIOSSimulator('TestNativeUnitTests', (String deviceId) async {
268+
if (!await runXcodeTests(
269+
platformDirectory: path.join(rootPath, 'ios'),
270+
destination: 'id=$deviceId',
271+
configuration: 'Debug',
272+
testName: 'native_plugin_unit_tests_ios',
273+
skipCodesign: true,
274+
)) {
275+
throw TaskResult.failure('Platform unit tests failed');
276+
}
277+
});
278+
break;
279+
case 'macos':
257280
if (!await runXcodeTests(
258-
platformDirectory: path.join(rootPath, 'ios'),
259-
destination: 'id=$deviceId',
281+
platformDirectory: path.join(rootPath, 'macos'),
282+
destination: 'platform=macOS',
260283
configuration: 'Debug',
261-
testName: 'native_plugin_unit_tests_ios',
284+
testName: 'native_plugin_unit_tests_macos',
262285
skipCodesign: true,
263286
)) {
264287
throw TaskResult.failure('Platform unit tests failed');
265288
}
266-
});
267-
} else if (buildTarget == 'macos') {
268-
if (!await runXcodeTests(
269-
platformDirectory: path.join(rootPath, 'macos'),
270-
destination: 'platform=macOS',
271-
configuration: 'Debug',
272-
testName: 'native_plugin_unit_tests_macos',
273-
skipCodesign: true,
274-
)) {
275-
throw TaskResult.failure('Platform unit tests failed');
276-
}
277-
} else if (buildTarget == 'windows') {
278-
if (await exec(
279-
path.join(rootPath, 'build', 'windows', 'plugins', 'plugintest', 'Release', 'plugintest_plugin_test'),
280-
<String>[],
281-
canFail: true,
282-
) != 0) {
283-
throw TaskResult.failure('Platform unit tests failed');
284-
}
289+
break;
290+
case 'windows':
291+
if (await exec(
292+
path.join(rootPath, 'build', 'windows', 'plugins', 'plugintest', 'Release', 'plugintest_plugin_test'),
293+
<String>[],
294+
canFail: true,
295+
) != 0) {
296+
throw TaskResult.failure('Platform unit tests failed');
297+
}
298+
break;
285299
}
286300
}
287301

packages/flutter_tools/templates/plugin/android-java.tmpl/build.gradle.tmpl

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,19 @@ android {
3232
defaultConfig {
3333
minSdkVersion {{minSdkVersion}}
3434
}
35+
36+
dependencies {
37+
testImplementation 'junit:junit:4.13.2'
38+
testImplementation 'org.mockito:mockito-core:5.0.0'
39+
}
40+
41+
testOptions {
42+
unitTests.all {
43+
testLogging {
44+
events "passed", "skipped", "failed", "standardOut", "standardError"
45+
outputs.upToDateWhen {false}
46+
showStandardStreams = true
47+
}
48+
}
49+
}
3550
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package {{androidIdentifier}};
2+
3+
import static org.mockito.Mockito.mock;
4+
import static org.mockito.Mockito.verify;
5+
6+
import io.flutter.plugin.common.MethodCall;
7+
import io.flutter.plugin.common.MethodChannel;
8+
import org.junit.Test;
9+
10+
/**
11+
* This demonstrates a simple unit test of the Java portion of this plugin's implementation.
12+
*
13+
* Once you have built the plugin's example app, you can run these tests from the command
14+
* line by running `./gradlew testDebugUnitTest` in the `example/android/` directory, or
15+
* you can run them directly from IDEs that support JUnit such as Android Studio.
16+
*/
17+
18+
public class {{pluginClass}}Test {
19+
@Test
20+
public void onMethodCall_getPlatformVersion_returnsExpectedValue() {
21+
{{pluginClass}} plugin = new {{pluginClass}}();
22+
23+
final MethodCall call = new MethodCall("getPlatformVersion", null);
24+
MethodChannel.Result mockResult = mock(MethodChannel.Result.class);
25+
plugin.onMethodCall(call, mockResult);
26+
27+
verify(mockResult).success("Android " + android.os.Build.VERSION.RELEASE);
28+
}
29+
}

packages/flutter_tools/templates/plugin/android-kotlin.tmpl/build.gradle.tmpl

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,27 @@ android {
3838

3939
sourceSets {
4040
main.java.srcDirs += 'src/main/kotlin'
41+
test.java.srcDirs += 'src/test/kotlin'
4142
}
4243

4344
defaultConfig {
4445
minSdkVersion {{minSdkVersion}}
4546
}
47+
48+
dependencies {
49+
testImplementation 'org.jetbrains.kotlin:kotlin-test'
50+
testImplementation 'org.mockito:mockito-core:5.0.0'
51+
}
52+
53+
testOptions {
54+
unitTests.all {
55+
useJUnitPlatform()
56+
57+
testLogging {
58+
events "passed", "skipped", "failed", "standardOut", "standardError"
59+
outputs.upToDateWhen {false}
60+
showStandardStreams = true
61+
}
62+
}
63+
}
4664
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package {{androidIdentifier}}
2+
3+
import io.flutter.plugin.common.MethodCall
4+
import io.flutter.plugin.common.MethodChannel
5+
import kotlin.test.Test
6+
import org.mockito.Mockito
7+
8+
/*
9+
* This demonstrates a simple unit test of the Kotlin portion of this plugin's implementation.
10+
*
11+
* Once you have built the plugin's example app, you can run these tests from the command
12+
* line by running `./gradlew testDebugUnitTest` in the `example/android/` directory, or
13+
* you can run them directly from IDEs that support JUnit such as Android Studio.
14+
*/
15+
16+
internal class {{pluginClass}}Test {
17+
@Test
18+
fun onMethodCall_getPlatformVersion_returnsExpectedValue() {
19+
val plugin = {{pluginClass}}()
20+
21+
val call = MethodCall("getPlatformVersion", null)
22+
val mockResult: MethodChannel.Result = Mockito.mock(MethodChannel.Result::class.java)
23+
plugin.onMethodCall(call, mockResult)
24+
25+
Mockito.verify(mockResult).success("Android " + android.os.Build.VERSION.RELEASE)
26+
}
27+
}

packages/flutter_tools/templates/template_manifest.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,9 +251,11 @@
251251
"templates/plugin/android-java.tmpl/build.gradle.tmpl",
252252
"templates/plugin/android-java.tmpl/projectName_android.iml.tmpl",
253253
"templates/plugin/android-java.tmpl/src/main/java/androidIdentifier/pluginClass.java.tmpl",
254+
"templates/plugin/android-java.tmpl/src/test/java/androidIdentifier/pluginClassTest.java.tmpl",
254255
"templates/plugin/android-kotlin.tmpl/build.gradle.tmpl",
255256
"templates/plugin/android-kotlin.tmpl/projectName_android.iml.tmpl",
256257
"templates/plugin/android-kotlin.tmpl/src/main/kotlin/androidIdentifier/pluginClass.kt.tmpl",
258+
"templates/plugin/android-kotlin.tmpl/src/test/kotlin/androidIdentifier/pluginClassTest.kt.tmpl",
257259
"templates/plugin/android.tmpl/.gitignore",
258260
"templates/plugin/android.tmpl/gradle/wrapper/gradle-wrapper.properties",
259261
"templates/plugin/android.tmpl/gradle.properties.tmpl",

packages/flutter_tools/test/commands.shard/permeable/create_test.dart

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2452,6 +2452,63 @@ void main() {
24522452
Logger: () => logger,
24532453
});
24542454

2455+
testUsingContext('plugin includes native Kotlin unit tests', () async {
2456+
Cache.flutterRoot = '../..';
2457+
2458+
final CreateCommand command = CreateCommand();
2459+
final CommandRunner<void> runner = createTestCommandRunner(command);
2460+
2461+
await runner.run(<String>[
2462+
'create',
2463+
'--no-pub',
2464+
'--template=plugin',
2465+
'--org=com.example',
2466+
'--platforms=android',
2467+
projectDir.path]);
2468+
2469+
expect(projectDir
2470+
.childDirectory('android')
2471+
.childDirectory('src')
2472+
.childDirectory('test')
2473+
.childDirectory('kotlin')
2474+
.childDirectory('com')
2475+
.childDirectory('example')
2476+
.childDirectory('flutter_project')
2477+
.childFile('FlutterProjectPluginTest.kt'), exists);
2478+
}, overrides: <Type, Generator>{
2479+
FeatureFlags: () => TestFeatureFlags(),
2480+
Logger: () => logger,
2481+
});
2482+
2483+
testUsingContext('plugin includes native Java unit tests', () async {
2484+
Cache.flutterRoot = '../..';
2485+
2486+
final CreateCommand command = CreateCommand();
2487+
final CommandRunner<void> runner = createTestCommandRunner(command);
2488+
2489+
await runner.run(<String>[
2490+
'create',
2491+
'--no-pub',
2492+
'--template=plugin',
2493+
'--org=com.example',
2494+
'--platforms=android',
2495+
'-a', 'java',
2496+
projectDir.path]);
2497+
2498+
expect(projectDir
2499+
.childDirectory('android')
2500+
.childDirectory('src')
2501+
.childDirectory('test')
2502+
.childDirectory('java')
2503+
.childDirectory('com')
2504+
.childDirectory('example')
2505+
.childDirectory('flutter_project')
2506+
.childFile('FlutterProjectPluginTest.java'), exists);
2507+
}, overrides: <Type, Generator>{
2508+
FeatureFlags: () => TestFeatureFlags(),
2509+
Logger: () => logger,
2510+
});
2511+
24552512
testUsingContext('plugin includes native Objective-C unit tests', () async {
24562513
Cache.flutterRoot = '../..';
24572514

0 commit comments

Comments
 (0)