Compare commits
72 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c2d456da97 | ||
|
|
28500558fc | ||
|
|
990f407ebe | ||
|
|
f6206ee294 | ||
|
|
8cfba333ff | ||
|
|
397fc3d2fa | ||
|
|
b7a076dfaf | ||
|
|
aee2ec2767 | ||
|
|
682d822249 | ||
|
|
6cf3f993ca | ||
|
|
0cce5a6ec0 | ||
|
|
0917406c44 | ||
|
|
3bf19ee6d0 | ||
|
|
e1afb1cd54 | ||
|
|
10953ff02a | ||
|
|
80b0f3b878 | ||
|
|
8fbd459280 | ||
|
|
f3309f933e | ||
|
|
b47204e329 | ||
|
|
6616ff1174 | ||
|
|
09ecd0d7c0 | ||
|
|
93db467f43 | ||
|
|
a78931ae81 | ||
|
|
93ad1fa94f | ||
|
|
2780ccc6fa | ||
|
|
fd3b088f59 | ||
|
|
e812aa6126 | ||
|
|
b949f57d54 | ||
|
|
0551240c7d | ||
|
|
8a3a0cf6bf | ||
|
|
d3ab246c78 | ||
|
|
142b0e5a1e | ||
|
|
38221a67ef | ||
|
|
993c178adf | ||
|
|
3786c6251e | ||
|
|
e5f8e5244c | ||
|
|
e13dce7ac6 | ||
|
|
3265803dec | ||
|
|
ef3891bd57 | ||
|
|
fcbdb7275b | ||
|
|
ea4c6c5b3e | ||
|
|
381ff52515 | ||
|
|
c5f39dfae3 | ||
|
|
7f8ef70e57 | ||
|
|
23af64475a | ||
|
|
7181b49211 | ||
|
|
cd7b9cc922 | ||
|
|
46288fa0e9 | ||
|
|
e61597ba8b | ||
|
|
c577301493 | ||
|
|
a33908db41 | ||
|
|
4ea97fe13f | ||
|
|
7cda8ffd36 | ||
|
|
1da98a0364 | ||
|
|
c6d892da2d | ||
|
|
d1f2c3da97 | ||
|
|
f40cfe929a | ||
|
|
96d1f9d633 | ||
|
|
350802fc9b | ||
|
|
0ae5163435 | ||
|
|
0e581a7eeb | ||
|
|
b68ff99800 | ||
|
|
7754f828cc | ||
|
|
3d62c91291 | ||
|
|
14c3d841c5 | ||
|
|
5f6cf3888c | ||
|
|
75c6a67b8f | ||
|
|
d11402dd6c | ||
|
|
db771b9c66 | ||
|
|
338c7828c8 | ||
|
|
8ebe2a5d0c | ||
|
|
4f3e3f0093 |
12
README.md
@@ -2,7 +2,7 @@
|
||||
|
||||
换种思路写Mock,让单元测试更简单。
|
||||
|
||||
无需初始化,不挑服务框架,甭管要换的是私有方法、静态方法、构造方法还是其他任何类的任何方法,也甭管要换的对象是怎么创建的。写好Mock定义,加个`@MockMethod`注解,一切统统搞定。
|
||||
无需初始化,不挑服务框架,甭管要换的是私有方法、静态方法、构造方法还是其他任何类的任何方法,也甭管要换的对象是怎么创建的。写好Mock定义,加个`@MockInvoke`注解,一切统统搞定。
|
||||
|
||||
- 文档:https://alibaba.github.io/testable-mock/
|
||||
- 国内文档镜像:http://freyrlin.gitee.io/testable-mock/
|
||||
@@ -11,17 +11,13 @@
|
||||
|
||||
<font size="5">**0.6版本已发布**</font>,从`0.5.x`升级到`0.6.x`版本请参考[版本升级指南](https://alibaba.github.io/testable-mock/#/zh-cn/doc/upgrade-guide)
|
||||
|
||||
如果有遇到其他任何使用问题和建议,请直接在[Issues](https://github.com/alibaba/testable-mock/issues)中提出,也可通过[Pull Request](https://github.com/alibaba/testable-mock/pulls)提交您的代码,我们将在24小时内回复并处理
|
||||
|
||||
-----
|
||||
|
||||
## 版本计划
|
||||
## 项目维护说明
|
||||
|
||||
`TestableMock`正在持续迭代演进,以下版本计划可能在开发过程中发生调整
|
||||
由于当前并行项目较多,此项目暂时转入维护阶段,在此期间`TestableMock`会继续提供不定期的版本更新。
|
||||
|
||||
- `0.6` 当前版本,进行中的工作内容参考[Issue](https://github.com/alibaba/testable-mock/issues)清单
|
||||
- `0.7` 完善Mock功能的防代码重构机制,支持快速Mock指定类型的所有方法
|
||||
- `1.0` 功能稳定,一个崭新的开始
|
||||
如果有遇到其他任何使用问题和建议,请直接在[Issues](https://github.com/alibaba/testable-mock/issues)中提出,也可通过[Pull Request](https://github.com/alibaba/testable-mock/pulls)提交您的代码,我们将尽快回复并处理。
|
||||
|
||||
## 目录结构
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
Write mock faster, make unit testing easier.
|
||||
|
||||
Any test framework, no initialization, no matter private method, static method, construction method, or any other method of any class, and no matter how the object created.
|
||||
Write a mock method, add an `@MockMethod` annotation, everything is done.
|
||||
Write a mock method, add an `@MockInvoke` annotation, everything is done.
|
||||
|
||||
Usage Document: https://alibaba.github.io/testable-mock/#/en-us/
|
||||
|
||||
@@ -12,7 +12,7 @@ Usage Document: https://alibaba.github.io/testable-mock/#/en-us/
|
||||
`TestableMock` is still under heavy development, the following version plans may be adjusted during the iteration
|
||||
|
||||
- `v0.6` it's the current version, refer to the [issue](https://github.com/alibaba/testable-mock/issues) list for the work in progress
|
||||
- `v0.7` better anti-refactor support of mocking, allow quick mock all method in specified class
|
||||
- `v0.7` better anti-refactoring support of mocking, allow quick mock all method in specified class
|
||||
- `v1.0` all functions are stable, a brand-new start
|
||||
|
||||
## Directory Structure
|
||||
|
||||
15
demo/android-demo/.gitignore
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
*.iml
|
||||
.gradle
|
||||
/local.properties
|
||||
/.idea/caches
|
||||
/.idea/libraries
|
||||
/.idea/modules.xml
|
||||
/.idea/workspace.xml
|
||||
/.idea/navEditor.xml
|
||||
/.idea/assetWizardSettings.xml
|
||||
.DS_Store
|
||||
/build
|
||||
/captures
|
||||
.externalNativeBuild
|
||||
.cxx
|
||||
local.properties
|
||||
1
demo/android-demo/app/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/build
|
||||
55
demo/android-demo/app/build.gradle
Normal file
@@ -0,0 +1,55 @@
|
||||
plugins {
|
||||
id 'com.android.application'
|
||||
id 'kotlin-android'
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion 30
|
||||
buildToolsVersion "30.0.3"
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.alibaba.testable.demo"
|
||||
minSdkVersion 30
|
||||
targetSdkVersion 30
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = '1.8'
|
||||
}
|
||||
testOptions {
|
||||
unitTests {
|
||||
includeAndroidResources = true
|
||||
all {
|
||||
jvmArgs "-javaagent:${classpath.find { it.name.contains("testable-agent") }.absolutePath}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||
implementation 'androidx.core:core-ktx:1.2.0'
|
||||
implementation 'androidx.appcompat:appcompat:1.3.0-rc01'
|
||||
implementation 'com.google.android.material:material:1.1.0'
|
||||
testImplementation 'androidx.test:core:1.4.0-alpha05'
|
||||
testImplementation 'androidx.test:runner:1.4.0-alpha05'
|
||||
testImplementation 'junit:junit:4.+'
|
||||
testImplementation 'org.robolectric:robolectric:4.5.1'
|
||||
testImplementation 'com.alibaba.testable:testable-all:0.6.10'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
|
||||
}
|
||||
21
demo/android-demo/app/proguard-rules.pro
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.alibaba.testable.demo
|
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
import org.junit.Assert.*
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
*
|
||||
* See [testing documentation](http://d.android.com/tools/testing).
|
||||
*/
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class ExampleInstrumentedTest {
|
||||
@Test
|
||||
fun useAppContext() {
|
||||
// Context of the app under test.
|
||||
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
|
||||
assertEquals("com.alibaba.testable.demo", appContext.packageName)
|
||||
}
|
||||
}
|
||||
13
demo/android-demo/app/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.alibaba.testable.demo">
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.TestableAndroidDemo" />
|
||||
|
||||
</manifest>
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.alibaba.testable.demo;
|
||||
|
||||
import com.alibaba.testable.demo.model.BlackBox;
|
||||
|
||||
/**
|
||||
* 演示基本的Mock功能
|
||||
* Demonstrate basic mock functionality
|
||||
*/
|
||||
public class DemoBasic {
|
||||
|
||||
/**
|
||||
* method with new operation
|
||||
*/
|
||||
public String newFunc() {
|
||||
BlackBox component = new BlackBox("something");
|
||||
return component.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* method with member method invoke
|
||||
*/
|
||||
public String outerFunc(String s) throws Exception {
|
||||
return "{ \"res\": \"" + innerFunc(s) + staticFunc() + "\"}";
|
||||
}
|
||||
|
||||
/**
|
||||
* method with common method invoke
|
||||
*/
|
||||
public String commonFunc() {
|
||||
return "anything".trim() + "__" + "anything".substring(1, 2) + "__" + "abc".startsWith("ab");
|
||||
}
|
||||
|
||||
/**
|
||||
* method with static method invoke
|
||||
*/
|
||||
public BlackBox getBox() {
|
||||
return BlackBox.secretBox();
|
||||
}
|
||||
|
||||
/**
|
||||
* two methods invoke same private method
|
||||
*/
|
||||
public String callerOne() {
|
||||
return callFromDifferentMethod();
|
||||
}
|
||||
|
||||
public String callerTwo() {
|
||||
return callFromDifferentMethod();
|
||||
}
|
||||
|
||||
private static String staticFunc() {
|
||||
return "_STATIC_TAIL";
|
||||
}
|
||||
|
||||
private String innerFunc(String s) {
|
||||
return "Nothing";
|
||||
}
|
||||
|
||||
private String callFromDifferentMethod() {
|
||||
return "realOne";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.alibaba.testable.demo;
|
||||
|
||||
import android.app.Service;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.IBinder;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* 演示与Robolectric配合使用
|
||||
* Demonstrate using mock along with Robolectric
|
||||
*/
|
||||
public class DemoService extends Service {
|
||||
|
||||
static final String TAG = "DemoService";
|
||||
SharedPreferences mPreference;
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
if(intent != null && "start_foreground".equalsIgnoreCase(intent.getAction())) {
|
||||
Log.d(TAG, "start service.");
|
||||
} else if(intent != null && "stop_foreground".equalsIgnoreCase(intent.getAction())) {
|
||||
Log.d(TAG, "stop service.");
|
||||
}
|
||||
return START_REDELIVER_INTENT;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.alibaba.testable.demo.model;
|
||||
|
||||
public class BlackBox extends Box implements Color {
|
||||
|
||||
public BlackBox(String data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public static BlackBox secretBox() {
|
||||
return new BlackBox("secret");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(String something) {
|
||||
data = something;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getColor() {
|
||||
return "black";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.alibaba.testable.demo.model;
|
||||
|
||||
abstract public class Box {
|
||||
|
||||
protected String data;
|
||||
|
||||
abstract public void put(String something);
|
||||
|
||||
public String get() {
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.alibaba.testable.demo.model;
|
||||
|
||||
public interface Color {
|
||||
|
||||
String getColor();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:endX="85.84757"
|
||||
android:endY="92.4963"
|
||||
android:startX="42.9492"
|
||||
android:startY="49.59793"
|
||||
android:type="linear">
|
||||
<item
|
||||
android:color="#44000000"
|
||||
android:offset="0.0" />
|
||||
<item
|
||||
android:color="#00000000"
|
||||
android:offset="1.0" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
|
||||
android:strokeWidth="1"
|
||||
android:strokeColor="#00000000" />
|
||||
</vector>
|
||||
@@ -0,0 +1,170 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:fillColor="#3DDC84"
|
||||
android:pathData="M0,0h108v108h-108z" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M9,0L9,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,0L19,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,0L29,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,0L39,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,0L49,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,0L59,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,0L69,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,0L79,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M89,0L89,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M99,0L99,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,9L108,9"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,19L108,19"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,29L108,29"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,39L108,39"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,49L108,49"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,59L108,59"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,69L108,69"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,79L108,79"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,89L108,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,99L108,99"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,29L89,29"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,39L89,39"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,49L89,49"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,59L89,59"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,69L89,69"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,79L89,79"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,19L29,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,19L39,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,19L49,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,19L59,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,19L69,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,19L79,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
</vector>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
||||
BIN
demo/android-demo/app/src/main/res/mipmap-hdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 5.2 KiB |
BIN
demo/android-demo/app/src/main/res/mipmap-mdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
BIN
demo/android-demo/app/src/main/res/mipmap-xhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 4.8 KiB |
|
After Width: | Height: | Size: 7.3 KiB |
BIN
demo/android-demo/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 7.7 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 16 KiB |
16
demo/android-demo/app/src/main/res/values-night/themes.xml
Normal file
@@ -0,0 +1,16 @@
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<!-- Base application theme. -->
|
||||
<style name="Theme.TestableAndroidDemo" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
|
||||
<!-- Primary brand color. -->
|
||||
<item name="colorPrimary">@color/purple_200</item>
|
||||
<item name="colorPrimaryVariant">@color/purple_700</item>
|
||||
<item name="colorOnPrimary">@color/black</item>
|
||||
<!-- Secondary brand color. -->
|
||||
<item name="colorSecondary">@color/teal_200</item>
|
||||
<item name="colorSecondaryVariant">@color/teal_200</item>
|
||||
<item name="colorOnSecondary">@color/black</item>
|
||||
<!-- Status bar color. -->
|
||||
<item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
|
||||
<!-- Customize your theme here. -->
|
||||
</style>
|
||||
</resources>
|
||||
10
demo/android-demo/app/src/main/res/values/colors.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="purple_200">#FFBB86FC</color>
|
||||
<color name="purple_500">#FF6200EE</color>
|
||||
<color name="purple_700">#FF3700B3</color>
|
||||
<color name="teal_200">#FF03DAC5</color>
|
||||
<color name="teal_700">#FF018786</color>
|
||||
<color name="black">#FF000000</color>
|
||||
<color name="white">#FFFFFFFF</color>
|
||||
</resources>
|
||||
3
demo/android-demo/app/src/main/res/values/strings.xml
Normal file
@@ -0,0 +1,3 @@
|
||||
<resources>
|
||||
<string name="app_name">Testable Android Demo</string>
|
||||
</resources>
|
||||
16
demo/android-demo/app/src/main/res/values/themes.xml
Normal file
@@ -0,0 +1,16 @@
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<!-- Base application theme. -->
|
||||
<style name="Theme.TestableAndroidDemo" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
|
||||
<!-- Primary brand color. -->
|
||||
<item name="colorPrimary">@color/purple_500</item>
|
||||
<item name="colorPrimaryVariant">@color/purple_700</item>
|
||||
<item name="colorOnPrimary">@color/white</item>
|
||||
<!-- Secondary brand color. -->
|
||||
<item name="colorSecondary">@color/teal_200</item>
|
||||
<item name="colorSecondaryVariant">@color/teal_700</item>
|
||||
<item name="colorOnSecondary">@color/black</item>
|
||||
<!-- Status bar color. -->
|
||||
<item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
|
||||
<!-- Customize your theme here. -->
|
||||
</style>
|
||||
</resources>
|
||||
@@ -0,0 +1,122 @@
|
||||
package com.alibaba.testable.demo;
|
||||
|
||||
import com.alibaba.testable.demo.model.BlackBox;
|
||||
import com.alibaba.testable.core.annotation.MockNew;
|
||||
import com.alibaba.testable.core.annotation.MockInvoke;
|
||||
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import static com.alibaba.testable.core.matcher.InvocationVerifier.verifyInvoked;
|
||||
import static com.alibaba.testable.core.tool.TestableTool.MOCK_CONTEXT;
|
||||
import static com.alibaba.testable.core.tool.TestableTool.SOURCE_METHOD;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* 演示基本的Mock功能
|
||||
* Demonstrate basic mock functionality
|
||||
*/
|
||||
public class DemoBasicTest {
|
||||
|
||||
private DemoBasic demoBasic = new DemoBasic();
|
||||
|
||||
public static class Mock {
|
||||
@MockNew
|
||||
private BlackBox createBlackBox(String text) {
|
||||
return new BlackBox("mock_" + text);
|
||||
}
|
||||
|
||||
@MockInvoke(targetClass = DemoBasic.class)
|
||||
private String innerFunc(String text) {
|
||||
return "mock_" + text;
|
||||
}
|
||||
|
||||
@MockInvoke(targetClass = DemoBasic.class)
|
||||
private String staticFunc() {
|
||||
return "_MOCK_TAIL";
|
||||
}
|
||||
|
||||
@MockInvoke(targetClass = String.class)
|
||||
private String trim() {
|
||||
return "trim_string";
|
||||
}
|
||||
|
||||
@MockInvoke(targetClass = String.class, targetMethod = "substring")
|
||||
private String sub(int i, int j) {
|
||||
return "sub_string";
|
||||
}
|
||||
|
||||
@MockInvoke(targetClass = String.class)
|
||||
private boolean startsWith(String s) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@MockInvoke(targetClass = BlackBox.class)
|
||||
private BlackBox secretBox() {
|
||||
return new BlackBox("not_secret_box");
|
||||
}
|
||||
|
||||
@MockInvoke(targetClass = DemoBasic.class)
|
||||
private String callFromDifferentMethod() {
|
||||
if ("special_case".equals(MOCK_CONTEXT.get("case"))) {
|
||||
return "mock_special";
|
||||
}
|
||||
switch (SOURCE_METHOD) {
|
||||
case "callerOne":
|
||||
return "mock_one";
|
||||
default:
|
||||
return "mock_others";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_mock_new_object() {
|
||||
assertEquals("mock_something", demoBasic.newFunc());
|
||||
verifyInvoked("createBlackBox").with("something");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_mock_member_method() throws Exception {
|
||||
assertEquals("{ \"res\": \"mock_hello_MOCK_TAIL\"}", demoBasic.outerFunc("hello"));
|
||||
verifyInvoked("innerFunc").with("hello");
|
||||
verifyInvoked("staticFunc").with();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_mock_common_method() {
|
||||
assertEquals("trim_string__sub_string__false", demoBasic.commonFunc());
|
||||
verifyInvoked("trim").withTimes(1);
|
||||
verifyInvoked("sub").withTimes(1);
|
||||
verifyInvoked("startsWith").withTimes(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_mock_static_method() {
|
||||
assertEquals("not_secret_box", demoBasic.getBox().get());
|
||||
verifyInvoked("secretBox").withTimes(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_get_source_method_name() throws Exception {
|
||||
// synchronous
|
||||
assertEquals("mock_one_mock_others", demoBasic.callerOne() + "_" + demoBasic.callerTwo());
|
||||
// asynchronous
|
||||
assertEquals("mock_one_mock_others",
|
||||
Executors.newSingleThreadExecutor().submit(() -> demoBasic.callerOne() + "_" + demoBasic.callerTwo()).get());
|
||||
verifyInvoked("callFromDifferentMethod").withTimes(4);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_set_mock_context() throws Exception {
|
||||
MOCK_CONTEXT.put("case", "special_case");
|
||||
// synchronous
|
||||
assertEquals("mock_special", demoBasic.callerOne());
|
||||
// asynchronous
|
||||
assertEquals("mock_special", Executors.newSingleThreadExecutor().submit(() -> demoBasic.callerOne()).get());
|
||||
verifyInvoked("callFromDifferentMethod").withTimes(2);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.alibaba.testable.demo;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.util.Log;
|
||||
|
||||
import com.alibaba.testable.core.annotation.MockInvoke;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.Robolectric;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
import static com.alibaba.testable.core.matcher.InvocationVerifier.verifyInvoked;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(sdk = 30)
|
||||
public class DemoServiceTest {
|
||||
|
||||
private DemoService demoService;
|
||||
|
||||
public static class Mock {
|
||||
@MockInvoke(targetClass = Log.class, targetMethod = "d")
|
||||
public static int log(String tag, String msg) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
demoService = Robolectric.setupService(DemoService.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnStartCommand() {
|
||||
Intent intent = new Intent();
|
||||
|
||||
intent.setAction("start_foreground");
|
||||
demoService.onStartCommand(intent, 0, 1);
|
||||
verifyInvoked("log").with("DemoService", "start service.");
|
||||
|
||||
intent.setAction("stop_foreground");
|
||||
demoService.onStartCommand(intent, 0, 1);
|
||||
verifyInvoked("log").with("DemoService", "stop service.");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
log.file = null
|
||||
28
demo/android-demo/build.gradle
Normal file
@@ -0,0 +1,28 @@
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
buildscript {
|
||||
ext.kotlin_version = "1.4.32"
|
||||
repositories {
|
||||
mavenLocal()
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath "com.android.tools.build:gradle:7.0.0-alpha14"
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
mavenLocal()
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
}
|
||||
|
||||
task clean(type: Delete) {
|
||||
delete rootProject.buildDir
|
||||
}
|
||||
21
demo/android-demo/gradle.properties
Normal file
@@ -0,0 +1,21 @@
|
||||
# Project-wide Gradle settings.
|
||||
# IDE (e.g. Android Studio) users:
|
||||
# Gradle settings configured through the IDE *will override*
|
||||
# any settings specified in this file.
|
||||
# For more details on how to configure your build environment visit
|
||||
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||
# Specifies the JVM arguments used for the daemon process.
|
||||
# The setting is particularly useful for tweaking memory settings.
|
||||
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
|
||||
# When configured, Gradle will run in incubating parallel mode.
|
||||
# This option should only be used with decoupled projects. More details, visit
|
||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||
# org.gradle.parallel=true
|
||||
# AndroidX package structure to make it clearer which packages are bundled with the
|
||||
# Android operating system, and which are packaged with your app"s APK
|
||||
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
||||
android.useAndroidX=true
|
||||
# Automatically convert third-party libraries to use AndroidX
|
||||
android.enableJetifier=true
|
||||
# Kotlin code style for this project: "official" or "obsolete":
|
||||
kotlin.code.style=official
|
||||
6
demo/android-demo/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
#Fri Apr 30 14:35:40 CST 2021
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0-bin.zip
|
||||
172
demo/android-demo/gradlew
vendored
Executable file
@@ -0,0 +1,172 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS=""
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=$((i+1))
|
||||
done
|
||||
case $i in
|
||||
(0) set -- ;;
|
||||
(1) set -- "$args0" ;;
|
||||
(2) set -- "$args0" "$args1" ;;
|
||||
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=$(save "$@")
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
||||
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
||||
cd "$(dirname "$0")"
|
||||
fi
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
84
demo/android-demo/gradlew.bat
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windows variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
2
demo/android-demo/settings.gradle
Normal file
@@ -0,0 +1,2 @@
|
||||
include ':app'
|
||||
rootProject.name = "Testable Android Demo"
|
||||
@@ -13,8 +13,8 @@ repositories {
|
||||
|
||||
dependencies {
|
||||
testImplementation('org.junit.jupiter:junit-jupiter:5.6.2')
|
||||
testImplementation('com.alibaba.testable:testable-all:0.6.4')
|
||||
testAnnotationProcessor('com.alibaba.testable:testable-processor:0.6.4')
|
||||
testImplementation('com.alibaba.testable:testable-all:0.6.10')
|
||||
testAnnotationProcessor('com.alibaba.testable:testable-processor:0.6.10')
|
||||
}
|
||||
|
||||
tasks.withType(JavaCompile) {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
<junit.version>5.6.2</junit.version>
|
||||
<testable.version>0.6.4</testable.version>
|
||||
<testable.version>0.6.10</testable.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.alibaba.demo.association;
|
||||
|
||||
/**
|
||||
* 目标类,此类中的一些调用将会被Mock掉
|
||||
* Target class, some invocations inside this class will be mocked
|
||||
*/
|
||||
public class CookerService {
|
||||
|
||||
private static String hireSandwichCooker() {
|
||||
return "Real-Sandwich-Cooker";
|
||||
}
|
||||
|
||||
private static String hireHamburgerCooker() {
|
||||
return "Real-Hamburger-Cooker";
|
||||
}
|
||||
|
||||
private String cookSandwich() {
|
||||
return "Real-Sandwich";
|
||||
}
|
||||
|
||||
private String cookHamburger() {
|
||||
return "Real-Hamburger";
|
||||
}
|
||||
|
||||
public String prepareSandwich() {
|
||||
return hireSandwichCooker() + " & " + cookSandwich();
|
||||
}
|
||||
|
||||
public String prepareHamburger() {
|
||||
return hireHamburgerCooker() + " & " + cookHamburger();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.alibaba.demo.association;
|
||||
|
||||
/**
|
||||
* 被测类,会访问`CookerService`里的方法
|
||||
* Class to be tested, which will access methods in TargetService class
|
||||
*/
|
||||
public class SellerService {
|
||||
|
||||
private CookerService cookerService = new CookerService();
|
||||
|
||||
public String sellSandwich() {
|
||||
return cookerService.prepareSandwich();
|
||||
}
|
||||
|
||||
public String sellHamburger() {
|
||||
return cookerService.prepareHamburger();
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ import com.alibaba.demo.basic.model.mock.Color;
|
||||
public class DemoInherit {
|
||||
|
||||
/**
|
||||
* call method overridden by sub class via parent class variable
|
||||
* call method overridden by subclass via parent class variable
|
||||
*/
|
||||
public Box putIntoBox() {
|
||||
Box box = new BlackBox("");
|
||||
@@ -20,7 +20,7 @@ public class DemoInherit {
|
||||
}
|
||||
|
||||
/**
|
||||
* call method overridden by sub class via sub class variable
|
||||
* call method overridden by subclass via subclass variable
|
||||
*/
|
||||
public BlackBox putIntoBlackBox() {
|
||||
BlackBox box = new BlackBox("");
|
||||
@@ -37,7 +37,7 @@ public class DemoInherit {
|
||||
}
|
||||
|
||||
/**
|
||||
* call method defined in parent class via sub class variable
|
||||
* call method defined in parent class via subclass variable
|
||||
*/
|
||||
public String getFromBlackBox() {
|
||||
BlackBox box = new BlackBox("data");
|
||||
@@ -53,10 +53,18 @@ public class DemoInherit {
|
||||
}
|
||||
|
||||
/**
|
||||
* call method defined in interface via sub class variable
|
||||
* call method defined in interface via subclass variable
|
||||
*/
|
||||
public String getColorViaBox() {
|
||||
BlackBox box = new BlackBox("");
|
||||
return box.getColor();
|
||||
}
|
||||
|
||||
/**
|
||||
* call method defined in interface via subclass variable
|
||||
*/
|
||||
public String getColorIdxViaColor() {
|
||||
Color color = new BlackBox("");
|
||||
return color.getColorIndex();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,9 @@ public class DemoPrivateAccess {
|
||||
* private member method with arguments
|
||||
*/
|
||||
private String privateFuncWithArgs(List<String> list, String str, int i) {
|
||||
return list.stream().reduce((a, s) -> a + s).orElse("") + " + " + str + " + " + i;
|
||||
return list.stream().reduce((a, s) -> a + s).orElse("")
|
||||
+ " + " + (str == null ? "null" : str)
|
||||
+ " + " + i;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.alibaba.demo.basic.model.mock;
|
||||
|
||||
public interface BasicColor {
|
||||
|
||||
String getColorIndex();
|
||||
|
||||
}
|
||||
@@ -20,4 +20,8 @@ public class BlackBox extends Box implements Color {
|
||||
return "black";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getColorIndex() {
|
||||
return "idx";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.alibaba.demo.basic.model.mock;
|
||||
|
||||
public interface Color {
|
||||
public interface Color extends BasicColor {
|
||||
|
||||
String getColor();
|
||||
|
||||
|
||||
@@ -0,0 +1,243 @@
|
||||
package com.alibaba.demo.lambda;
|
||||
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* @author jimca
|
||||
*/
|
||||
@SuppressWarnings({"WrapperTypeMayBePrimitive", "ResultOfMethodCallIgnored", "MismatchedReadAndWriteOfArray", "unused"})
|
||||
public class ExternalLambdaDemo {
|
||||
|
||||
public void string1() {
|
||||
String s = "";
|
||||
consumesFunction2(s::contains);
|
||||
}
|
||||
|
||||
public void string2() {
|
||||
String s = "";
|
||||
consumesFunction2(s::charAt);
|
||||
|
||||
}
|
||||
|
||||
public void string3() {
|
||||
String s = "";
|
||||
consumesFunction0(s::notify);
|
||||
}
|
||||
|
||||
public void byte1() {
|
||||
Byte s = 1;
|
||||
consumesSupplier(s::floatValue);
|
||||
}
|
||||
|
||||
public void byte2() {
|
||||
Byte s = 1;
|
||||
consumesFunction2(s::compareTo);
|
||||
}
|
||||
|
||||
public void byte3() {
|
||||
Byte s = 1;
|
||||
consumesFunction0(s::notify);
|
||||
}
|
||||
|
||||
public void char1() {
|
||||
Character s = 1;
|
||||
consumesFunction0(s::toString);
|
||||
}
|
||||
|
||||
public void char2() {
|
||||
Character s = 1;
|
||||
consumesFunction2(s::compareTo);
|
||||
}
|
||||
|
||||
public void char3() {
|
||||
Character s = 1;
|
||||
consumesFunction0(s::notify);
|
||||
}
|
||||
|
||||
public void short1() {
|
||||
Short s = 1;
|
||||
consumesFunction0(s::toString);
|
||||
}
|
||||
|
||||
public void short2() {
|
||||
Short s = 1;
|
||||
consumesFunction2(s::compareTo);
|
||||
}
|
||||
|
||||
public void short3() {
|
||||
Short s = 1;
|
||||
consumesFunction0(s::notify);
|
||||
}
|
||||
|
||||
public void int1() {
|
||||
Integer s = 1;
|
||||
consumesFunction0(s::toString);
|
||||
}
|
||||
|
||||
public void int2() {
|
||||
Integer s = 1;
|
||||
consumesFunction2(s::compareTo);
|
||||
}
|
||||
|
||||
public void int3() {
|
||||
Integer s = 1;
|
||||
consumesFunction0(s::notify);
|
||||
}
|
||||
|
||||
public void long1() {
|
||||
Long s = 1L;
|
||||
consumesFunction0(s::toString);
|
||||
}
|
||||
|
||||
public void long2() {
|
||||
Long s = 1L;
|
||||
consumesFunction2(s::compareTo);
|
||||
}
|
||||
|
||||
public void long3() {
|
||||
Long s = 1L;
|
||||
consumesFunction0(s::notify);
|
||||
}
|
||||
|
||||
public void float1() {
|
||||
Float s = 1f;
|
||||
consumesFunction0(s::toString);
|
||||
}
|
||||
|
||||
public void float2() {
|
||||
Float s = 1f;
|
||||
consumesFunction2(s::compareTo);
|
||||
}
|
||||
|
||||
public void float3() {
|
||||
Float s = 1f;
|
||||
consumesFunction0(s::notify);
|
||||
}
|
||||
|
||||
public void double1() {
|
||||
Double s = 1d;
|
||||
consumesFunction0(s::toString);
|
||||
}
|
||||
|
||||
public void double2() {
|
||||
Double s = 1d;
|
||||
consumesFunction2(s::compareTo);
|
||||
}
|
||||
|
||||
public void double3() {
|
||||
Double s = 1d;
|
||||
consumesFunction0(s::notify);
|
||||
}
|
||||
|
||||
public void bool1() {
|
||||
Boolean s = true;
|
||||
consumesFunction0(s::toString);
|
||||
}
|
||||
|
||||
public void bool2() {
|
||||
Boolean s = true;
|
||||
consumesFunction2(s::compareTo);
|
||||
}
|
||||
|
||||
public void bool3() {
|
||||
Boolean s = true;
|
||||
consumesFunction0(s::notify);
|
||||
}
|
||||
|
||||
public void stringArray1() {
|
||||
String[] array = new String[]{""};
|
||||
consumesFunction0(array::toString);
|
||||
}
|
||||
|
||||
public void stringArray2() {
|
||||
String[] array = new String[]{""};
|
||||
consumesFunction2(array::equals);
|
||||
}
|
||||
|
||||
public void stringArray3() {
|
||||
String[] array = new String[]{""};
|
||||
consumesFunction0(array::notify);
|
||||
}
|
||||
|
||||
public void intArray1() {
|
||||
int[] array = new int[]{1};
|
||||
consumesFunction0(array::toString);
|
||||
}
|
||||
|
||||
public void intArray2() {
|
||||
int[] array = new int[]{1};
|
||||
consumesFunction2(array::equals);
|
||||
}
|
||||
|
||||
public void intArray3() {
|
||||
int[] array = new int[]{1};
|
||||
consumesFunction0(array::notify);
|
||||
}
|
||||
|
||||
|
||||
public void mul() {
|
||||
String s = "";
|
||||
consumesTwoFunction2(s::contains, s::contains);
|
||||
}
|
||||
|
||||
|
||||
public void externalClass() {
|
||||
LambdaDemo lambdaDemo = new LambdaDemo();
|
||||
consumesFunction0(lambdaDemo::methodReference0);
|
||||
}
|
||||
|
||||
|
||||
public void interClass() {
|
||||
A a = new A();
|
||||
consumesFunction2(a::m1);
|
||||
consumesFunction2(a::m2);
|
||||
}
|
||||
|
||||
public void function3() {
|
||||
ExternalLambdaDemo externalLambdaDemo = new ExternalLambdaDemo();
|
||||
consumesFunction3(externalLambdaDemo::f3);
|
||||
}
|
||||
|
||||
public Boolean f3(String s1, Long l) {
|
||||
return false;
|
||||
}
|
||||
|
||||
private void consumesFunction0(Runnable f) {
|
||||
f.run();
|
||||
}
|
||||
|
||||
private <T> void consumesFunction1(Consumer<T> f) {
|
||||
f.accept(null);
|
||||
}
|
||||
|
||||
private <T, R> void consumesFunction2(Function<T, R> f) {
|
||||
f.apply(null);
|
||||
}
|
||||
|
||||
private <T1, T2, R> void consumesFunction3(BiFunction<T1, T2, R> f) {
|
||||
f.apply(null, null);
|
||||
}
|
||||
|
||||
private <T> void consumesSupplier(Supplier<T> supplier) {
|
||||
supplier.get();
|
||||
}
|
||||
|
||||
private <T, R> void consumesTwoFunction2(Function<T, R> f1, Function<T, R> f2) {
|
||||
f1.apply(null);
|
||||
f2.apply(null);
|
||||
}
|
||||
|
||||
public static class A {
|
||||
public String m1(int i) {
|
||||
return "";
|
||||
}
|
||||
|
||||
public String m2(Integer i) {
|
||||
return "";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.alibaba.demo.lambda;
|
||||
|
||||
/**
|
||||
* @author jim
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface Function1Throwable<T, R> {
|
||||
R apply(T t) throws Throwable;
|
||||
}
|
||||
@@ -0,0 +1,218 @@
|
||||
package com.alibaba.demo.lambda;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* @author jim
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class LambdaDemo {
|
||||
|
||||
public void methodReference() {
|
||||
consumesRun(this::run);
|
||||
}
|
||||
|
||||
private void consumesRun(Runnable r) {
|
||||
r.run();
|
||||
}
|
||||
|
||||
private void run() {
|
||||
blackHole();
|
||||
}
|
||||
|
||||
public String methodReference0() {
|
||||
return consumes0(this::function0);
|
||||
}
|
||||
|
||||
private String consumes0(Supplier<String> function0) {
|
||||
return function0.get();
|
||||
}
|
||||
|
||||
private String function0() {
|
||||
return "Hello";
|
||||
}
|
||||
|
||||
public String methodReference1() {
|
||||
return consumes1(this::function1);
|
||||
}
|
||||
|
||||
private String consumes1(Function<Integer, String> function) {
|
||||
return function.apply(1);
|
||||
}
|
||||
|
||||
private String function1(Integer i) {
|
||||
return String.valueOf(i);
|
||||
}
|
||||
|
||||
public String methodReferenceThrows() {
|
||||
return consumesThrows(this::function1Throwable);
|
||||
}
|
||||
|
||||
private String consumesThrows(Function1Throwable<Integer, String> function) {
|
||||
try {
|
||||
return function.apply(1);
|
||||
}catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("RedundantThrows")
|
||||
private String function1Throwable(Integer i) throws Throwable{
|
||||
return String.valueOf(i);
|
||||
}
|
||||
|
||||
public String methodReference2() {
|
||||
return consumes2(this::function2);
|
||||
}
|
||||
|
||||
private String consumes2(BiFunction<Integer, Double, String> function) {
|
||||
return function.apply(1, .2);
|
||||
}
|
||||
|
||||
private String function2(Integer i, Double d) {
|
||||
return i + String.valueOf(d);
|
||||
}
|
||||
|
||||
public String staticMethodReference1() {
|
||||
return consumes1(StaticMethod::function1);
|
||||
}
|
||||
|
||||
public String staticMethodReference2() {
|
||||
return consumes2(StaticMethod::function2);
|
||||
}
|
||||
|
||||
public void lambdaRun() {
|
||||
consumes(() -> System.out.println("lambdaRun"));
|
||||
}
|
||||
|
||||
private void consumes(Runnable o) {
|
||||
o.run();
|
||||
}
|
||||
|
||||
public void methodReferenceNew() {
|
||||
Object o = consumes(Object::new);
|
||||
blackHole(o);
|
||||
}
|
||||
|
||||
private <T> T consumes(Supplier<T> s) {
|
||||
return s.get();
|
||||
}
|
||||
|
||||
private void blackHole(Object... ignore) {}
|
||||
|
||||
public void array() {
|
||||
Function<Boolean[], Boolean[]> arrayBooleanFunction = this::arrayBooleanFunction;
|
||||
Function<boolean[], boolean[]> arrayBooleanFunction1 = this::arrayBoolFunction;
|
||||
Function<Byte[], Byte[]> byteFunction = this::arrayByteFunction;
|
||||
Function<byte[], byte[]> byteFunction1 = this::arrayByteFunction;
|
||||
Function<Character[], Character[]> charFunction = this::arrayCharFunction;
|
||||
Function<char[], char[]> charFunction1 = this::arrayCharFunction;
|
||||
Function<Short[], Short[]> shortFunction = this::arrayShortFunction;
|
||||
Function<short[], short[]> shortFunction1 = this::arrayShortFunction;
|
||||
Function<int[], int[]> intFunction = this::arrayIntFunction;
|
||||
Function<Integer[], Integer[]> intFunction1 = this::arrayIntegerFunction;
|
||||
Function<long[], long[]> longFunction = this::arrayLongFunction;
|
||||
Function<Long[], Long[]> longFunction1 = this::arrayLongFunction;
|
||||
Function<Float[], Float[]> floatFunction = this::arrayFloatFunction;
|
||||
Function<float[], float[]> floatFunction1 = this::arrayFloatFunction;
|
||||
Function<Double[], Double[]> doubleFunction = this::arrayDoubleFunction;
|
||||
Function<double[], double[]> doubleFunction1 = this::arrayDoubleFunction;
|
||||
blackHole(arrayBooleanFunction, arrayBooleanFunction1,
|
||||
byteFunction, byteFunction1, charFunction, charFunction1, shortFunction, shortFunction1,
|
||||
intFunction, intFunction1, longFunction, longFunction1, floatFunction, floatFunction1, doubleFunction,
|
||||
doubleFunction1
|
||||
);
|
||||
}
|
||||
|
||||
private int[] arrayIntFunction(int[] arg) {
|
||||
return arg;
|
||||
}
|
||||
|
||||
private Integer[] arrayIntegerFunction(Integer[] arg) {
|
||||
return arg;
|
||||
}
|
||||
|
||||
private boolean[] arrayBoolFunction(boolean[] arg) {
|
||||
return arg;
|
||||
}
|
||||
|
||||
private Boolean[] arrayBooleanFunction(Boolean[] arg) {
|
||||
return arg;
|
||||
}
|
||||
|
||||
private byte[] arrayByteFunction(byte[] arg) {
|
||||
return arg;
|
||||
}
|
||||
|
||||
private Byte[] arrayByteFunction(Byte[] arg) {
|
||||
return arg;
|
||||
}
|
||||
|
||||
private char[] arrayCharFunction(char[] arg) {
|
||||
return arg;
|
||||
}
|
||||
|
||||
private Character[] arrayCharFunction(Character[] arg) {
|
||||
return arg;
|
||||
}
|
||||
|
||||
private short[] arrayShortFunction(short[] arg) {
|
||||
return arg;
|
||||
}
|
||||
|
||||
private Short[] arrayShortFunction(Short[] arg) {
|
||||
return arg;
|
||||
}
|
||||
|
||||
private long[] arrayLongFunction(long[] arg) {
|
||||
return arg;
|
||||
}
|
||||
|
||||
private Long[] arrayLongFunction(Long[] arg) {
|
||||
return arg;
|
||||
}
|
||||
|
||||
private float[] arrayFloatFunction(float[] arg) {
|
||||
return arg;
|
||||
}
|
||||
|
||||
private Float[] arrayFloatFunction(Float[] arg) {
|
||||
return arg;
|
||||
}
|
||||
|
||||
private double[] arrayDoubleFunction(double[] arg) {
|
||||
return arg;
|
||||
}
|
||||
|
||||
private Double[] arrayDoubleFunction(Double[] arg) {
|
||||
return arg;
|
||||
}
|
||||
|
||||
public void generic() {
|
||||
Function<?, ?> genericFunction = this::genericFunction;
|
||||
blackHole(genericFunction);
|
||||
}
|
||||
|
||||
public <T, R> R genericFunction(T arg) {
|
||||
//noinspection unchecked
|
||||
return (R)arg;
|
||||
}
|
||||
|
||||
private void collects() {
|
||||
long l = Stream.of("1", "2", "3")
|
||||
.filter(v -> !"2".equals(v))
|
||||
.map(Long::parseLong)
|
||||
.peek(this::blackHole)
|
||||
.map(v -> new ArrayList<Long>(){{add(v);}})
|
||||
.flatMap(Collection::stream)
|
||||
.mapToLong(Long::valueOf)
|
||||
.sum();
|
||||
blackHole(l);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.alibaba.demo.lambda;
|
||||
|
||||
/**
|
||||
* @author jim
|
||||
*/
|
||||
public class StaticMethod {
|
||||
|
||||
public static String function1(Integer i) {
|
||||
return "static" + i;
|
||||
}
|
||||
|
||||
public static String function2(Integer i, Double d) {
|
||||
return "static" + i + d;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.alibaba.demo.association;
|
||||
|
||||
import com.alibaba.testable.core.annotation.MockInvoke;
|
||||
import com.alibaba.testable.core.model.MockScope;
|
||||
|
||||
class CookerServiceMock {
|
||||
|
||||
@MockInvoke(targetClass = CookerService.class)
|
||||
public static String hireSandwichCooker() {
|
||||
return "Fake-Sandwich-Cooker";
|
||||
}
|
||||
|
||||
@MockInvoke(targetClass = CookerService.class, scope = MockScope.ASSOCIATED)
|
||||
public static String hireHamburgerCooker() {
|
||||
return "Fake-Hamburger-Cooker";
|
||||
}
|
||||
|
||||
@MockInvoke(targetClass = CookerService.class)
|
||||
private String cookSandwich() {
|
||||
return "Faked-Sandwich";
|
||||
}
|
||||
|
||||
@MockInvoke(targetClass = CookerService.class, scope = MockScope.ASSOCIATED)
|
||||
private String cookHamburger() {
|
||||
return "Faked-Hamburger";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.alibaba.demo.association;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class SellerServiceTest {
|
||||
|
||||
private SellerService sellerService = new SellerService();
|
||||
|
||||
@Test
|
||||
void should_sell_sandwich() {
|
||||
assertEquals("Fake-Sandwich-Cooker & Faked-Sandwich", sellerService.sellSandwich());
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_sell_hamburger() {
|
||||
assertEquals("Real-Hamburger-Cooker & Real-Hamburger", sellerService.sellHamburger());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,10 +3,10 @@ package com.alibaba.demo.basic;
|
||||
import com.alibaba.demo.basic.model.mock.BlackBox;
|
||||
import com.alibaba.demo.basic.model.mock.Box;
|
||||
import com.alibaba.demo.basic.model.mock.Color;
|
||||
import com.alibaba.testable.core.annotation.MockMethod;
|
||||
import com.alibaba.testable.core.annotation.MockInvoke;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static com.alibaba.testable.core.matcher.InvokeVerifier.verify;
|
||||
import static com.alibaba.testable.core.matcher.InvocationVerifier.verifyInvoked;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
/**
|
||||
@@ -18,77 +18,88 @@ class DemoInheritTest {
|
||||
private DemoInherit demoInherit = new DemoInherit();
|
||||
|
||||
public static class Mock {
|
||||
@MockMethod(targetMethod = "put")
|
||||
@MockInvoke(targetMethod = "put")
|
||||
private void put_into_box(Box self, String something) {
|
||||
self.put("put_" + something + "_into_box");
|
||||
}
|
||||
|
||||
@MockMethod(targetMethod = "put")
|
||||
@MockInvoke(targetMethod = "put")
|
||||
private void put_into_blackbox(BlackBox self, String something) {
|
||||
self.put("put_" + something + "_into_blackbox");
|
||||
}
|
||||
|
||||
@MockMethod(targetMethod = "get")
|
||||
@MockInvoke(targetMethod = "get")
|
||||
private String get_from_box(Box self) {
|
||||
return "get_from_box";
|
||||
}
|
||||
|
||||
@MockMethod(targetMethod = "get")
|
||||
@MockInvoke(targetMethod = "get")
|
||||
private String get_from_blackbox(BlackBox self) {
|
||||
return "get_from_blackbox";
|
||||
}
|
||||
|
||||
@MockMethod(targetMethod = "getColor")
|
||||
@MockInvoke(targetMethod = "getColor")
|
||||
private String get_color_from_color(Color self) {
|
||||
return "color_from_color";
|
||||
}
|
||||
|
||||
@MockMethod(targetMethod = "getColor")
|
||||
@MockInvoke(targetMethod = "getColor")
|
||||
private String get_color_from_blackbox(BlackBox self) {
|
||||
return "color_from_blackbox";
|
||||
}
|
||||
|
||||
@MockInvoke(targetMethod = "getColorIndex")
|
||||
private String get_colorIdx_from_color(Color self) {
|
||||
return "colorIdx_from_color";
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_mock_call_sub_object_method_by_parent_object() {
|
||||
BlackBox box = (BlackBox)demoInherit.putIntoBox();
|
||||
verify("put_into_box").withTimes(1);
|
||||
verifyInvoked("put_into_box").withTimes(1);
|
||||
assertEquals("put_data_into_box", box.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_mock_call_sub_object_method_by_sub_object() {
|
||||
BlackBox box = demoInherit.putIntoBlackBox();
|
||||
verify("put_into_blackbox").withTimes(1);
|
||||
verifyInvoked("put_into_blackbox").withTimes(1);
|
||||
assertEquals("put_data_into_blackbox", box.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_mock_call_parent_object_method_by_parent_object() {
|
||||
String content = demoInherit.getFromBox();
|
||||
verify("get_from_box").withTimes(1);
|
||||
verifyInvoked("get_from_box").withTimes(1);
|
||||
assertEquals("get_from_box", content);
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_mock_call_parent_object_method_by_sub_object() {
|
||||
String content = demoInherit.getFromBlackBox();
|
||||
verify("get_from_blackbox").withTimes(1);
|
||||
verifyInvoked("get_from_blackbox").withTimes(1);
|
||||
assertEquals("get_from_blackbox", content);
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_mock_call_interface_method_by_interface_object() {
|
||||
String color = demoInherit.getColorViaColor();
|
||||
verify("get_color_from_color").withTimes(1);
|
||||
verifyInvoked("get_color_from_color").withTimes(1);
|
||||
assertEquals("color_from_color", color);
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_mock_call_interface_method_by_sub_class_object() {
|
||||
String color = demoInherit.getColorViaBox();
|
||||
verify("get_color_from_blackbox").withTimes(1);
|
||||
verifyInvoked("get_color_from_blackbox").withTimes(1);
|
||||
assertEquals("color_from_blackbox", color);
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_mock_call_interface_method_by_sub_interface_object() {
|
||||
String colorIdx = demoInherit.getColorIdxViaColor();
|
||||
verifyInvoked("get_colorIdx_from_color").withTimes(1);
|
||||
assertEquals("colorIdx_from_color", colorIdx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.alibaba.demo.basic;
|
||||
|
||||
import com.alibaba.testable.core.annotation.MockMethod;
|
||||
import com.alibaba.testable.core.annotation.MockInvoke;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
@@ -12,7 +12,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
class DemoInnerClassTest {
|
||||
|
||||
public static class Mock {
|
||||
@MockMethod(targetClass = DemoInnerClass.class)
|
||||
@MockInvoke(targetClass = DemoInnerClass.class)
|
||||
String methodToBeMock() {
|
||||
return "MockedCall";
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package com.alibaba.demo.basic;
|
||||
|
||||
import com.alibaba.demo.basic.model.mock.BlackBox;
|
||||
import com.alibaba.testable.core.annotation.MockMethod;
|
||||
import com.alibaba.testable.core.annotation.MockInvoke;
|
||||
import com.alibaba.testable.core.error.VerifyFailedError;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static com.alibaba.testable.core.matcher.InvokeMatcher.*;
|
||||
import static com.alibaba.testable.core.matcher.InvokeVerifier.verify;
|
||||
import static com.alibaba.testable.core.matcher.InvocationMatcher.*;
|
||||
import static com.alibaba.testable.core.matcher.InvocationVerifier.verifyInvoked;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
/**
|
||||
@@ -18,62 +18,62 @@ class DemoMatcherTest {
|
||||
private DemoMatcher demoMatcher = new DemoMatcher();
|
||||
|
||||
public static class Mock {
|
||||
@MockMethod(targetMethod = "methodToBeMocked")
|
||||
@MockInvoke(targetMethod = "methodToBeMocked")
|
||||
private void methodWithoutArgument(DemoMatcher self) {}
|
||||
|
||||
@MockMethod(targetMethod = "methodToBeMocked")
|
||||
@MockInvoke(targetMethod = "methodToBeMocked")
|
||||
private void methodWithArguments(DemoMatcher self, Object a1, Object a2) {}
|
||||
|
||||
@MockMethod(targetMethod = "methodToBeMocked")
|
||||
@MockInvoke(targetMethod = "methodToBeMocked")
|
||||
private void methodWithArrayArgument(DemoMatcher self, Object[] a) {}
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_match_no_argument() {
|
||||
demoMatcher.callMethodWithoutArgument();
|
||||
verify("methodWithoutArgument").withTimes(1);
|
||||
verifyInvoked("methodWithoutArgument").withTimes(1);
|
||||
demoMatcher.callMethodWithoutArgument();
|
||||
verify("methodWithoutArgument").withTimes(2);
|
||||
verifyInvoked("methodWithoutArgument").withTimes(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_match_number_arguments() {
|
||||
demoMatcher.callMethodWithNumberArguments();
|
||||
verify("methodWithArguments").without(anyString(), 2);
|
||||
verify("methodWithArguments").withInOrder(anyInt(), 2);
|
||||
verify("methodWithArguments").withInOrder(anyLong(), anyNumber());
|
||||
verify("methodWithArguments").with(1.0, anyMapOf(Integer.class, Float.class));
|
||||
verify("methodWithArguments").with(anyList(), anySetOf(Float.class));
|
||||
verify("methodWithArguments").with(anyList(), anyListOf(Float.class));
|
||||
verify("methodWithArrayArgument").with(anyArrayOf(Long.class));
|
||||
verify("methodWithArrayArgument").with(anyArray());
|
||||
verifyInvoked("methodWithArguments").without(anyString(), 2);
|
||||
verifyInvoked("methodWithArguments").withInOrder(anyInt(), 2);
|
||||
verifyInvoked("methodWithArguments").withInOrder(anyLong(), anyNumber());
|
||||
verifyInvoked("methodWithArguments").with(1.0, anyMapOf(Integer.class, Float.class));
|
||||
verifyInvoked("methodWithArguments").with(anyList(), anySetOf(Float.class));
|
||||
verifyInvoked("methodWithArguments").with(anyList(), anyListOf(Float.class));
|
||||
verifyInvoked("methodWithArrayArgument").with(anyArrayOf(Long.class));
|
||||
verifyInvoked("methodWithArrayArgument").with(anyArray());
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_match_string_arguments() {
|
||||
demoMatcher.callMethodWithStringArgument();
|
||||
verify("methodWithArguments").with(startsWith("he"), endsWith("ld"));
|
||||
verify("methodWithArguments").with(contains("stab"), matches("m.[cd]k"));
|
||||
verify("methodWithArrayArgument").with(anyArrayOf(String.class));
|
||||
verifyInvoked("methodWithArguments").with(startsWith("he"), endsWith("ld"));
|
||||
verifyInvoked("methodWithArguments").with(contains("stab"), matches("m.[cd]k"));
|
||||
verifyInvoked("methodWithArrayArgument").with(anyArrayOf(String.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_match_object_arguments() {
|
||||
demoMatcher.callMethodWithObjectArgument();
|
||||
verify("methodWithArguments").withInOrder(any(BlackBox.class), any(BlackBox.class));
|
||||
verify("methodWithArguments").withInOrder(nullable(BlackBox.class), nullable(BlackBox.class));
|
||||
verify("methodWithArguments").withInOrder(isNull(), notNull());
|
||||
verifyInvoked("methodWithArguments").withInOrder(any(BlackBox.class), any(BlackBox.class));
|
||||
verifyInvoked("methodWithArguments").withInOrder(nullable(BlackBox.class), nullable(BlackBox.class));
|
||||
verifyInvoked("methodWithArguments").withInOrder(isNull(), notNull());
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_match_with_times() {
|
||||
demoMatcher.callMethodWithNumberArguments();
|
||||
verify("methodWithArguments").with(anyNumber(), any()).times(3);
|
||||
verifyInvoked("methodWithArguments").with(anyNumber(), any()).times(3);
|
||||
|
||||
demoMatcher.callMethodWithNumberArguments();
|
||||
boolean gotError = false;
|
||||
try {
|
||||
verify("methodWithArguments").with(anyNumber(), any()).times(4);
|
||||
verifyInvoked("methodWithArguments").with(anyNumber(), any()).times(4);
|
||||
} catch (VerifyFailedError e) {
|
||||
gotError = true;
|
||||
}
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
package com.alibaba.demo.basic;
|
||||
|
||||
import com.alibaba.demo.basic.model.mock.BlackBox;
|
||||
import com.alibaba.testable.core.annotation.MockConstructor;
|
||||
import com.alibaba.testable.core.annotation.MockMethod;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import com.alibaba.testable.core.annotation.MockNew;
|
||||
import com.alibaba.testable.core.annotation.MockInvoke;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import static com.alibaba.testable.core.matcher.InvokeVerifier.verify;
|
||||
import static com.alibaba.testable.core.matcher.InvocationVerifier.verifyInvoked;
|
||||
import static com.alibaba.testable.core.tool.TestableTool.MOCK_CONTEXT;
|
||||
import static com.alibaba.testable.core.tool.TestableTool.SOURCE_METHOD;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
@@ -22,42 +21,42 @@ class DemoMockTest {
|
||||
private DemoMock demoMock = new DemoMock();
|
||||
|
||||
public static class Mock {
|
||||
@MockConstructor
|
||||
@MockNew
|
||||
private BlackBox createBlackBox(String text) {
|
||||
return new BlackBox("mock_" + text);
|
||||
}
|
||||
|
||||
@MockMethod(targetClass = DemoMock.class)
|
||||
@MockInvoke(targetClass = DemoMock.class)
|
||||
private String innerFunc(String text) {
|
||||
return "mock_" + text;
|
||||
}
|
||||
|
||||
@MockMethod(targetClass = DemoMock.class)
|
||||
@MockInvoke(targetClass = DemoMock.class)
|
||||
private String staticFunc() {
|
||||
return "_MOCK_TAIL";
|
||||
}
|
||||
|
||||
@MockMethod(targetClass = String.class)
|
||||
@MockInvoke(targetClass = String.class)
|
||||
private String trim() {
|
||||
return "trim_string";
|
||||
}
|
||||
|
||||
@MockMethod(targetClass = String.class, targetMethod = "substring")
|
||||
@MockInvoke(targetClass = String.class, targetMethod = "substring")
|
||||
private String sub(int i, int j) {
|
||||
return "sub_string";
|
||||
}
|
||||
|
||||
@MockMethod(targetClass = String.class)
|
||||
@MockInvoke(targetClass = String.class)
|
||||
private boolean startsWith(String s) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@MockMethod(targetClass = BlackBox.class)
|
||||
@MockInvoke(targetClass = BlackBox.class)
|
||||
private BlackBox secretBox() {
|
||||
return new BlackBox("not_secret_box");
|
||||
}
|
||||
|
||||
@MockMethod(targetClass = DemoMock.class)
|
||||
@MockInvoke(targetClass = DemoMock.class)
|
||||
private String callFromDifferentMethod() {
|
||||
if ("special_case".equals(MOCK_CONTEXT.get("case"))) {
|
||||
return "mock_special";
|
||||
@@ -74,28 +73,28 @@ class DemoMockTest {
|
||||
@Test
|
||||
void should_mock_new_object() {
|
||||
assertEquals("mock_something", demoMock.newFunc());
|
||||
verify("createBlackBox").with("something");
|
||||
verifyInvoked("createBlackBox").with("something");
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_mock_member_method() throws Exception {
|
||||
assertEquals("{ \"res\": \"mock_hello_MOCK_TAIL\"}", demoMock.outerFunc("hello"));
|
||||
verify("innerFunc").with("hello");
|
||||
verify("staticFunc").with();
|
||||
verifyInvoked("innerFunc").with("hello");
|
||||
verifyInvoked("staticFunc").with();
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_mock_common_method() {
|
||||
assertEquals("trim_string__sub_string__false", demoMock.commonFunc());
|
||||
verify("trim").withTimes(1);
|
||||
verify("sub").withTimes(1);
|
||||
verify("startsWith").withTimes(1);
|
||||
verifyInvoked("trim").withTimes(1);
|
||||
verifyInvoked("sub").withTimes(1);
|
||||
verifyInvoked("startsWith").withTimes(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_mock_static_method() {
|
||||
Assertions.assertEquals("not_secret_box", demoMock.getBox().get());
|
||||
verify("secretBox").withTimes(1);
|
||||
assertEquals("not_secret_box", demoMock.getBox().get());
|
||||
verifyInvoked("secretBox").withTimes(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -105,7 +104,7 @@ class DemoMockTest {
|
||||
// asynchronous
|
||||
assertEquals("mock_one_mock_others",
|
||||
Executors.newSingleThreadExecutor().submit(() -> demoMock.callerOne() + "_" + demoMock.callerTwo()).get());
|
||||
verify("callFromDifferentMethod").withTimes(4);
|
||||
verifyInvoked("callFromDifferentMethod").withTimes(4);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -115,7 +114,7 @@ class DemoMockTest {
|
||||
assertEquals("mock_special", demoMock.callerOne());
|
||||
// asynchronous
|
||||
assertEquals("mock_special", Executors.newSingleThreadExecutor().submit(() -> demoMock.callerOne()).get());
|
||||
verify("callFromDifferentMethod").withTimes(2);
|
||||
verifyInvoked("callFromDifferentMethod").withTimes(2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package com.alibaba.demo.basic;
|
||||
|
||||
import com.alibaba.testable.core.tool.PrivateAccessor;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static com.alibaba.testable.core.tool.PrivateAccessor.*;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
|
||||
@@ -20,39 +20,45 @@ class DemoPrivateAccessorTest {
|
||||
@Test
|
||||
void should_access_private_method() {
|
||||
List<String> list = new ArrayList<String>() {{ add("a"); add("b"); add("c"); }};
|
||||
assertEquals("member", PrivateAccessor.invoke(demoPrivateAccess, "privateFunc"));
|
||||
assertEquals("abc + hello + 1", PrivateAccessor.invoke(demoPrivateAccess, "privateFuncWithArgs", list, "hello", 1));
|
||||
assertEquals("member", invoke(demoPrivateAccess, "privateFunc"));
|
||||
assertEquals("abc + hello + 1", invoke(demoPrivateAccess, "privateFuncWithArgs", list, "hello", 1));
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_access_private_field() {
|
||||
PrivateAccessor.set(demoPrivateAccess, "count", 3);
|
||||
assertEquals(Integer.valueOf(3), PrivateAccessor.get(demoPrivateAccess, "count"));
|
||||
set(demoPrivateAccess, "count", 3);
|
||||
assertEquals(Integer.valueOf(3), get(demoPrivateAccess, "count"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_access_private_static_method() {
|
||||
assertEquals("static", PrivateAccessor.invokeStatic(DemoPrivateAccess.class, "privateStaticFunc"));
|
||||
assertEquals("hello + 1", PrivateAccessor.invokeStatic(DemoPrivateAccess.class, "privateStaticFuncWithArgs", "hello", 1));
|
||||
assertEquals("static", invokeStatic(DemoPrivateAccess.class, "privateStaticFunc"));
|
||||
assertEquals("hello + 1", invokeStatic(DemoPrivateAccess.class, "privateStaticFuncWithArgs", "hello", 1));
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_access_private_static_field() {
|
||||
PrivateAccessor.setStatic(DemoPrivateAccess.class, "staticCount", 3);
|
||||
assertEquals(Integer.valueOf(3), PrivateAccessor.getStatic(DemoPrivateAccess.class, "staticCount"));
|
||||
setStatic(DemoPrivateAccess.class, "staticCount", 3);
|
||||
assertEquals(Integer.valueOf(3), getStatic(DemoPrivateAccess.class, "staticCount"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_update_final_field() {
|
||||
PrivateAccessor.set(demoPrivateAccess, "pi", 3.14);
|
||||
assertEquals(Double.valueOf(3.14), PrivateAccessor.get(demoPrivateAccess, "pi"));
|
||||
set(demoPrivateAccess, "pi", 3.14);
|
||||
assertEquals(Double.valueOf(3.14), get(demoPrivateAccess, "pi"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_use_null_parameter() {
|
||||
PrivateAccessor.set(demoPrivateAccess, "pi", null);
|
||||
assertNull(PrivateAccessor.get(demoPrivateAccess, "pi"));
|
||||
assertEquals("null + 1", PrivateAccessor.invokeStatic(DemoPrivateAccess.class, "privateStaticFuncWithArgs", null, 1));
|
||||
set(demoPrivateAccess, "pi", null);
|
||||
assertNull(get(demoPrivateAccess, "pi"));
|
||||
|
||||
List<String> list = new ArrayList<String>() {{ add("a"); add("b"); add("c"); }};
|
||||
String value = invoke(demoPrivateAccess, "privateFuncWithArgs", list, null, 0);
|
||||
assertEquals("abc + null + 0", value);
|
||||
|
||||
value = invokeStatic(DemoPrivateAccess.class, "privateStaticFuncWithArgs", null, 1);
|
||||
assertEquals("null + 1", value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
package com.alibaba.demo.basic;
|
||||
|
||||
import com.alibaba.testable.core.annotation.MockConstructor;
|
||||
import com.alibaba.testable.core.annotation.MockMethod;
|
||||
import com.alibaba.demo.basic.DemoTemplate;
|
||||
import com.alibaba.testable.core.annotation.MockNew;
|
||||
import com.alibaba.testable.core.annotation.MockInvoke;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.*;
|
||||
@@ -21,24 +20,24 @@ class DemoTemplateTest {
|
||||
/* 第一种写法:使用泛型定义 */
|
||||
/* First solution: use generics type */
|
||||
|
||||
@MockMethod
|
||||
@MockInvoke
|
||||
private <T> List<T> getList(DemoTemplate self, T value) {
|
||||
return new ArrayList<T>() {{ add((T)(value.toString() + "_mock_list")); }};
|
||||
}
|
||||
|
||||
@MockMethod
|
||||
@MockInvoke
|
||||
private <K, V> Map<K, V> getMap(DemoTemplate self, K key, V value) {
|
||||
return new HashMap<K, V>() {{ put(key, (V)(value.toString() + "_mock_map")); }};
|
||||
}
|
||||
|
||||
@MockConstructor
|
||||
@MockNew
|
||||
private <T> HashSet<T> newHashSet() {
|
||||
HashSet<T> set = new HashSet<>();
|
||||
set.add((T)"insert_mock");
|
||||
return set;
|
||||
}
|
||||
|
||||
@MockMethod
|
||||
@MockInvoke
|
||||
private <E> boolean add(Set s, E e) {
|
||||
s.add(e.toString() + "_mocked");
|
||||
return true;
|
||||
@@ -47,24 +46,24 @@ class DemoTemplateTest {
|
||||
/* 第二种写法:使用Object类型 */
|
||||
/* Second solution: use object type */
|
||||
|
||||
//@MockMethod
|
||||
//@MockInvoke
|
||||
//private List<Object> getList(DemoTemplate self, Object value) {
|
||||
// return new ArrayList<Object>() {{ add(value.toString() + "_mock_list"); }};
|
||||
//}
|
||||
//
|
||||
//@MockMethod
|
||||
//@MockInvoke
|
||||
//private Map<Object, Object> getMap(DemoTemplate self, Object key, Object value) {
|
||||
// return new HashMap<Object, Object>() {{ put(key, value.toString() + "_mock_map"); }};
|
||||
//}
|
||||
//
|
||||
//@MockConstructor
|
||||
//@MockNew
|
||||
//private HashSet newHashSet() {
|
||||
// HashSet<Object> set = new HashSet<>();
|
||||
// set.add("insert_mock");
|
||||
// return set;
|
||||
//}
|
||||
//
|
||||
//@MockMethod
|
||||
//@MockInvoke
|
||||
//private boolean add(Set s, Object e) {
|
||||
// s.add(e.toString() + "_mocked");
|
||||
// return true;
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
package com.alibaba.demo.lambda;
|
||||
|
||||
import com.alibaba.testable.core.annotation.MockInvoke;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static com.alibaba.testable.core.matcher.InvocationVerifier.verifyInvoked;
|
||||
|
||||
/**
|
||||
* @author zcbbpo
|
||||
*/
|
||||
public class ExternalLambdaDemoTest {
|
||||
private final ExternalLambdaDemo lambdaDemo = new ExternalLambdaDemo();
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static class Mock {
|
||||
|
||||
@MockInvoke(targetClass = String.class, targetMethod = "contains")
|
||||
public boolean mockContains(CharSequence s) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@MockInvoke(targetClass = Byte.class, targetMethod = "floatValue")
|
||||
public float mockFloatValue() {
|
||||
return 0.1f;
|
||||
}
|
||||
|
||||
@MockInvoke(targetClass = Double.class, targetMethod = "compareTo")
|
||||
public int mockCompareTo(Double anotherDouble) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@MockInvoke(targetClass = LambdaDemo.class, targetMethod = "methodReference0")
|
||||
public String mockMethodReference0() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@MockInvoke(targetClass = ExternalLambdaDemo.class, targetMethod = "f3")
|
||||
public Boolean mockF3(String s1, Long l) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void shouldMockString1() {
|
||||
lambdaDemo.string1();
|
||||
verifyInvoked("mockContains").withTimes(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldMockByte1() {
|
||||
lambdaDemo.byte1();
|
||||
verifyInvoked("mockFloatValue").withTimes(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldMockDouble2() {
|
||||
lambdaDemo.double2();
|
||||
verifyInvoked("mockCompareTo").withTimes(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMul() {
|
||||
lambdaDemo.mul();
|
||||
verifyInvoked("mockContains").withTimes(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExternalClass() {
|
||||
lambdaDemo.externalClass();
|
||||
verifyInvoked("mockMethodReference0").withTimes(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFunction3() {
|
||||
lambdaDemo.function3();
|
||||
verifyInvoked("mockF3").withTimes(1);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package com.alibaba.demo.lambda;
|
||||
|
||||
import com.alibaba.testable.core.annotation.MockInvoke;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static com.alibaba.testable.core.matcher.InvocationVerifier.verifyInvoked;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
/**
|
||||
* @author zcbbpo
|
||||
*/
|
||||
public class LambdaDemoTest {
|
||||
private final LambdaDemo lambdaDemo = new LambdaDemo();
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static class Mock {
|
||||
@MockInvoke(targetClass = LambdaDemo.class, targetMethod = "run")
|
||||
private void mockRun() {
|
||||
}
|
||||
|
||||
@MockInvoke(targetClass = LambdaDemo.class)
|
||||
private String function0() {
|
||||
return "mock_function0";
|
||||
}
|
||||
|
||||
@MockInvoke(targetClass = LambdaDemo.class)
|
||||
private String function1(Integer i) {
|
||||
return "mock_function1";
|
||||
}
|
||||
|
||||
@MockInvoke(targetClass = LambdaDemo.class)
|
||||
private String function2(Integer i, Double d) {
|
||||
return "mock_function2";
|
||||
}
|
||||
|
||||
@SuppressWarnings("RedundantThrows")
|
||||
@MockInvoke(targetClass = LambdaDemo.class)
|
||||
private String function1Throwable(Integer i) throws Throwable{
|
||||
return "mock_function1Throwable";
|
||||
}
|
||||
|
||||
@MockInvoke(targetClass = StaticMethod.class, targetMethod = "function1")
|
||||
public static String staticFunction1(Integer i) {
|
||||
return "mock_staticFunction1";
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldMockRun() {
|
||||
lambdaDemo.methodReference();
|
||||
verifyInvoked("mockRun").withTimes(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldMockFunction0() {
|
||||
String s = lambdaDemo.methodReference0();
|
||||
assertEquals(s, "mock_function0");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldMockFunction1() {
|
||||
String s = lambdaDemo.methodReference1();
|
||||
assertEquals(s, "mock_function1");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldMockFunction2() {
|
||||
String s = lambdaDemo.methodReference2();
|
||||
assertEquals(s, "mock_function2");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldMockFunction1Throws() {
|
||||
String s = lambdaDemo.methodReferenceThrows();
|
||||
assertEquals(s, "mock_function1Throwable");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldMockStaticFunction1() {
|
||||
String s = lambdaDemo.staticMethodReference1();
|
||||
assertEquals(s, "mock_staticFunction1");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
package com.alibaba.demo.one2multi;
|
||||
|
||||
import com.alibaba.testable.core.annotation.MockMethod;
|
||||
import com.alibaba.testable.core.annotation.MockInvoke;
|
||||
|
||||
public class ASvcMock {
|
||||
|
||||
@MockMethod(targetClass = String.class, targetMethod = "format")
|
||||
@MockInvoke(targetClass = String.class, targetMethod = "format")
|
||||
public String a_format(String format, Object... args) {
|
||||
return "a_mock";
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package com.alibaba.demo.one2multi;
|
||||
|
||||
import com.alibaba.testable.core.annotation.MockMethod;
|
||||
import com.alibaba.testable.core.annotation.MockInvoke;
|
||||
|
||||
public class BSvcMock {
|
||||
|
||||
@MockMethod(targetClass = String.class, targetMethod = "format")
|
||||
@MockInvoke(targetClass = String.class, targetMethod = "format")
|
||||
public String b_format(String format, Object... args) {
|
||||
return "b_mock";
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package com.alibaba.demo.one2multi;
|
||||
|
||||
import com.alibaba.testable.core.annotation.MockMethod;
|
||||
import com.alibaba.testable.core.annotation.MockInvoke;
|
||||
|
||||
public class CSvcMock {
|
||||
|
||||
@MockMethod(targetClass = String.class, targetMethod = "format")
|
||||
@MockInvoke(targetClass = String.class, targetMethod = "format")
|
||||
public String c_format(String format, Object... args) {
|
||||
return "c_mock";
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import com.alibaba.testable.core.annotation.MockWith;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
|
||||
import static com.alibaba.testable.core.matcher.InvokeVerifier.verify;
|
||||
import static com.alibaba.testable.core.matcher.InvocationVerifier.verifyInvoked;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
@MockWith
|
||||
@@ -19,9 +19,9 @@ public class OneToMultiSvcTest {
|
||||
assertEquals("a_mock", aSvc.demo("test"));
|
||||
assertEquals("b_mock", bSvc.demo("test"));
|
||||
assertEquals("c_mock", cSvc.demo("test"));
|
||||
verify("a_format").withTimes(1);
|
||||
verify("b_format").withTimes(1);
|
||||
verify("c_format").withTimes(1);
|
||||
verifyInvoked("a_format").withTimes(1);
|
||||
verifyInvoked("b_format").withTimes(1);
|
||||
verifyInvoked("c_format").withTimes(1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
omni.constructor.enhance.enable = true
|
||||
mock.target.checking.enable = true
|
||||
|
||||
@@ -9,6 +9,7 @@ version = "1.0.0-SNAPSHOT"
|
||||
java.sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
|
||||
repositories {
|
||||
mavenLocal()
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
@@ -16,8 +17,8 @@ dependencies {
|
||||
implementation("org.jetbrains.kotlin:kotlin-reflect")
|
||||
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
||||
testImplementation("org.junit.jupiter:junit-jupiter:5.6.2")
|
||||
testImplementation("com.alibaba.testable:testable-all:0.6.4")
|
||||
testAnnotationProcessor("com.alibaba.testable:testable-processor:0.6.4")
|
||||
testImplementation("com.alibaba.testable:testable-all:0.6.10")
|
||||
testAnnotationProcessor("com.alibaba.testable:testable-processor:0.6.10")
|
||||
}
|
||||
|
||||
tasks.withType<KotlinCompile> {
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
<junit.version>5.6.2</junit.version>
|
||||
<testable.version>0.6.4</testable.version>
|
||||
<testable.version>0.6.10</testable.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.alibaba.demo.association
|
||||
|
||||
/**
|
||||
* 目标类,此类中的一些调用将会被Mock掉
|
||||
* Target class, some invocations inside this class will be mocked
|
||||
*/
|
||||
class CookerService {
|
||||
|
||||
private fun cookSandwich(): String {
|
||||
return "Real-Sandwich"
|
||||
}
|
||||
|
||||
private fun cookHamburger(): String {
|
||||
return "Real-Hamburger"
|
||||
}
|
||||
|
||||
fun prepareSandwich(): String {
|
||||
return hireSandwichCooker() + " & " + cookSandwich()
|
||||
}
|
||||
|
||||
fun prepareHamburger(): String {
|
||||
return hireHamburgerCooker() + " & " + cookHamburger()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private fun hireSandwichCooker(): String {
|
||||
return "Real-Sandwich-Cooker"
|
||||
}
|
||||
|
||||
private fun hireHamburgerCooker(): String {
|
||||
return "Real-Hamburger-Cooker"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.alibaba.demo.association
|
||||
|
||||
/**
|
||||
* 被测类,会访问`CookerService`里的方法
|
||||
* Class to be tested, which will access methods in TargetService class
|
||||
*/
|
||||
class SellerService {
|
||||
|
||||
private val cookerService = CookerService()
|
||||
|
||||
fun sellSandwich(): String {
|
||||
return cookerService.prepareSandwich()
|
||||
}
|
||||
|
||||
fun sellHamburger(): String {
|
||||
return cookerService.prepareHamburger()
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ import com.alibaba.demo.basic.model.mock.Color
|
||||
class DemoInherit {
|
||||
|
||||
/**
|
||||
* call method overridden by sub class via parent class variable
|
||||
* call method overridden by subclass via parent class variable
|
||||
*/
|
||||
fun putIntoBox(): Box {
|
||||
val box: Box = BlackBox("")
|
||||
@@ -20,7 +20,7 @@ class DemoInherit {
|
||||
}
|
||||
|
||||
/**
|
||||
* call method overridden by sub class via sub class variable
|
||||
* call method overridden by subclass via subclass variable
|
||||
*/
|
||||
fun putIntoBlackBox(): BlackBox {
|
||||
val box = BlackBox("")
|
||||
@@ -38,7 +38,7 @@ class DemoInherit {
|
||||
}
|
||||
|
||||
/**
|
||||
* call method defined in parent class via sub class variable
|
||||
* call method defined in parent class via subclass variable
|
||||
*/
|
||||
val fromBlackBox: String?
|
||||
get() {
|
||||
@@ -56,7 +56,7 @@ class DemoInherit {
|
||||
}
|
||||
|
||||
/**
|
||||
* call method defined in interface via sub class variable
|
||||
* call method defined in interface via subclass variable
|
||||
*/
|
||||
val colorViaBox: String
|
||||
get() {
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.alibaba.demo.association
|
||||
|
||||
import com.alibaba.testable.core.annotation.MockInvoke
|
||||
import com.alibaba.testable.core.model.MockScope
|
||||
|
||||
internal class CookerServiceMock {
|
||||
|
||||
@MockInvoke(targetClass = CookerService::class)
|
||||
private fun cookSandwich(): String {
|
||||
return "Faked-Sandwich"
|
||||
}
|
||||
|
||||
@MockInvoke(targetClass = CookerService::class, scope = MockScope.ASSOCIATED)
|
||||
private fun cookHamburger(): String {
|
||||
return "Faked-Hamburger"
|
||||
}
|
||||
|
||||
@MockInvoke(targetClass = CookerService::class)
|
||||
fun hireSandwichCooker(): String {
|
||||
return "Fake-Sandwich-Cooker"
|
||||
}
|
||||
|
||||
@MockInvoke(targetClass = CookerService::class, scope = MockScope.ASSOCIATED)
|
||||
fun hireHamburgerCooker(): String {
|
||||
return "Fake-Hamburger-Cooker"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.alibaba.demo.association
|
||||
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
internal class SellerServiceTest {
|
||||
|
||||
private val sellerService = SellerService()
|
||||
|
||||
@Test
|
||||
fun should_sell_sandwich() {
|
||||
assertEquals("Fake-Sandwich-Cooker & Faked-Sandwich", sellerService.sellSandwich())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun should_sell_hamburger() {
|
||||
assertEquals("Real-Hamburger-Cooker & Real-Hamburger", sellerService.sellHamburger())
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
package com.alibaba.demo.basic
|
||||
|
||||
import com.alibaba.testable.core.annotation.MockMethod
|
||||
import com.alibaba.testable.core.matcher.InvokeVerifier
|
||||
import com.alibaba.testable.core.annotation.MockInvoke
|
||||
import com.alibaba.testable.core.matcher.InvocationVerifier
|
||||
import com.alibaba.demo.basic.model.mock.BlackBox
|
||||
import com.alibaba.demo.basic.model.mock.Box
|
||||
import com.alibaba.demo.basic.model.mock.Color
|
||||
import org.junit.jupiter.api.Assertions
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
/**
|
||||
@@ -14,35 +14,35 @@ import org.junit.jupiter.api.Test
|
||||
*/
|
||||
internal class DemoInheritTest {
|
||||
|
||||
private val demoInherit = com.alibaba.demo.basic.DemoInherit()
|
||||
private val demoInherit = DemoInherit()
|
||||
|
||||
class Mock {
|
||||
@MockMethod(targetMethod = "put")
|
||||
@MockInvoke(targetMethod = "put")
|
||||
private fun put_into_box(self: Box, something: String) {
|
||||
self.put("put_" + something + "_into_box")
|
||||
}
|
||||
|
||||
@MockMethod(targetMethod = "put")
|
||||
@MockInvoke(targetMethod = "put")
|
||||
private fun put_into_blackbox(self: BlackBox, something: String) {
|
||||
self.put("put_" + something + "_into_blackbox")
|
||||
}
|
||||
|
||||
@MockMethod(targetMethod = "get")
|
||||
@MockInvoke(targetMethod = "get")
|
||||
private fun get_from_box(self: Box): String {
|
||||
return "get_from_box"
|
||||
}
|
||||
|
||||
@MockMethod(targetMethod = "get")
|
||||
@MockInvoke(targetMethod = "get")
|
||||
private fun get_from_blackbox(self: BlackBox): String {
|
||||
return "get_from_blackbox"
|
||||
}
|
||||
|
||||
@MockMethod(targetMethod = "getColor")
|
||||
@MockInvoke(targetMethod = "getColor")
|
||||
private fun get_color_from_color(self: Color): String {
|
||||
return "color_from_color"
|
||||
}
|
||||
|
||||
@MockMethod(targetMethod = "getColor")
|
||||
@MockInvoke(targetMethod = "getColor")
|
||||
private fun get_color_from_blackbox(self: BlackBox): String {
|
||||
return "color_from_blackbox"
|
||||
}
|
||||
@@ -51,42 +51,42 @@ internal class DemoInheritTest {
|
||||
@Test
|
||||
fun should_mock_call_sub_object_method_by_parent_object() {
|
||||
val box = demoInherit.putIntoBox() as BlackBox
|
||||
InvokeVerifier.verify("put_into_box").withTimes(1)
|
||||
Assertions.assertEquals("put_data_into_box", box.get())
|
||||
InvocationVerifier.verifyInvoked("put_into_box").withTimes(1)
|
||||
assertEquals("put_data_into_box", box.get())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun should_mock_call_sub_object_method_by_sub_object() {
|
||||
val box = demoInherit.putIntoBlackBox()
|
||||
InvokeVerifier.verify("put_into_blackbox").withTimes(1)
|
||||
Assertions.assertEquals("put_data_into_blackbox", box.get())
|
||||
InvocationVerifier.verifyInvoked("put_into_blackbox").withTimes(1)
|
||||
assertEquals("put_data_into_blackbox", box.get())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun should_mock_call_parent_object_method_by_parent_object() {
|
||||
val content = demoInherit.fromBox
|
||||
InvokeVerifier.verify("get_from_box").withTimes(1)
|
||||
Assertions.assertEquals("get_from_box", content)
|
||||
InvocationVerifier.verifyInvoked("get_from_box").withTimes(1)
|
||||
assertEquals("get_from_box", content)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun should_mock_call_parent_object_method_by_sub_object() {
|
||||
val content = demoInherit.fromBlackBox
|
||||
InvokeVerifier.verify("get_from_blackbox").withTimes(1)
|
||||
Assertions.assertEquals("get_from_blackbox", content)
|
||||
InvocationVerifier.verifyInvoked("get_from_blackbox").withTimes(1)
|
||||
assertEquals("get_from_blackbox", content)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun should_mock_call_interface_method_by_interface_object() {
|
||||
val color = demoInherit.colorViaColor
|
||||
InvokeVerifier.verify("get_color_from_color").withTimes(1)
|
||||
Assertions.assertEquals("color_from_color", color)
|
||||
InvocationVerifier.verifyInvoked("get_color_from_color").withTimes(1)
|
||||
assertEquals("color_from_color", color)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun should_mock_call_interface_method_by_sub_class_object() {
|
||||
val color = demoInherit.colorViaBox
|
||||
InvokeVerifier.verify("get_color_from_blackbox").withTimes(1)
|
||||
Assertions.assertEquals("color_from_blackbox", color)
|
||||
InvocationVerifier.verifyInvoked("get_color_from_blackbox").withTimes(1)
|
||||
assertEquals("color_from_blackbox", color)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.alibaba.demo.basic
|
||||
|
||||
import com.alibaba.testable.core.annotation.MockMethod
|
||||
import org.junit.jupiter.api.Assertions
|
||||
import com.alibaba.testable.core.annotation.MockInvoke
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
/**
|
||||
@@ -11,7 +11,7 @@ import org.junit.jupiter.api.Test
|
||||
internal class DemoInnerClassTest {
|
||||
|
||||
class Mock {
|
||||
@MockMethod(targetClass = DemoInnerClass::class)
|
||||
@MockInvoke(targetClass = DemoInnerClass::class)
|
||||
fun methodToBeMock(): String {
|
||||
return "MockedCall"
|
||||
}
|
||||
@@ -21,8 +21,8 @@ internal class DemoInnerClassTest {
|
||||
@Throws(Exception::class)
|
||||
fun should_mock_invoke_inside_inner_class() {
|
||||
val demo = DemoInnerClass()
|
||||
Assertions.assertEquals("MockedCall", demo.callInnerDemo())
|
||||
Assertions.assertEquals("MockedCall", demo.callAnonymousInner())
|
||||
Assertions.assertEquals("MockedCall", DemoInnerClass.StaticInner().demo())
|
||||
assertEquals("MockedCall", demo.callInnerDemo())
|
||||
assertEquals("MockedCall", demo.callAnonymousInner())
|
||||
assertEquals("MockedCall", DemoInnerClass.StaticInner().demo())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package com.alibaba.demo.basic
|
||||
|
||||
import com.alibaba.testable.core.annotation.MockMethod
|
||||
import com.alibaba.testable.core.annotation.MockInvoke
|
||||
import com.alibaba.testable.core.error.VerifyFailedError
|
||||
import com.alibaba.testable.core.matcher.InvokeMatcher
|
||||
import com.alibaba.testable.core.matcher.InvokeVerifier
|
||||
import com.alibaba.testable.core.matcher.InvocationMatcher
|
||||
import com.alibaba.testable.core.matcher.InvocationVerifier
|
||||
import com.alibaba.demo.basic.model.mock.BlackBox
|
||||
import org.junit.jupiter.api.Assertions
|
||||
import org.junit.jupiter.api.Test
|
||||
@@ -14,69 +14,69 @@ import org.junit.jupiter.api.Test
|
||||
*/
|
||||
internal class DemoMatcherTest {
|
||||
|
||||
private val demoMatcher = com.alibaba.demo.basic.DemoMatcher()
|
||||
private val demoMatcher = DemoMatcher()
|
||||
|
||||
class Mock {
|
||||
@MockMethod(targetMethod = "methodToBeMocked")
|
||||
private fun methodWithoutArgument(self: com.alibaba.demo.basic.DemoMatcher) {
|
||||
@MockInvoke(targetMethod = "methodToBeMocked")
|
||||
private fun methodWithoutArgument(self: DemoMatcher) {
|
||||
}
|
||||
|
||||
@MockMethod(targetMethod = "methodToBeMocked")
|
||||
private fun methodWithArguments(self: com.alibaba.demo.basic.DemoMatcher, a1: Any, a2: Any) {
|
||||
@MockInvoke(targetMethod = "methodToBeMocked")
|
||||
private fun methodWithArguments(self: DemoMatcher, a1: Any, a2: Any) {
|
||||
}
|
||||
|
||||
@MockMethod(targetMethod = "methodToBeMocked")
|
||||
private fun methodWithArrayArgument(self: com.alibaba.demo.basic.DemoMatcher, a: Array<Any>) {
|
||||
@MockInvoke(targetMethod = "methodToBeMocked")
|
||||
private fun methodWithArrayArgument(self: DemoMatcher, a: Array<Any>) {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun should_match_no_argument() {
|
||||
demoMatcher.callMethodWithoutArgument()
|
||||
InvokeVerifier.verify("methodWithoutArgument").withTimes(1)
|
||||
InvocationVerifier.verifyInvoked("methodWithoutArgument").withTimes(1)
|
||||
demoMatcher.callMethodWithoutArgument()
|
||||
InvokeVerifier.verify("methodWithoutArgument").withTimes(2)
|
||||
InvocationVerifier.verifyInvoked("methodWithoutArgument").withTimes(2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun should_match_number_arguments() {
|
||||
demoMatcher.callMethodWithNumberArguments()
|
||||
InvokeVerifier.verify("methodWithArguments").without(InvokeMatcher.anyString(), 2)
|
||||
InvokeVerifier.verify("methodWithArguments").withInOrder(InvokeMatcher.anyInt(), 2)
|
||||
InvokeVerifier.verify("methodWithArguments").withInOrder(InvokeMatcher.anyLong(), InvokeMatcher.anyNumber())
|
||||
InvocationVerifier.verifyInvoked("methodWithArguments").without(InvocationMatcher.anyString(), 2)
|
||||
InvocationVerifier.verifyInvoked("methodWithArguments").withInOrder(InvocationMatcher.anyInt(), 2)
|
||||
InvocationVerifier.verifyInvoked("methodWithArguments").withInOrder(InvocationMatcher.anyLong(), InvocationMatcher.anyNumber())
|
||||
// Note: Must use `::class.javaObjectType` for primary types check in Kotlin
|
||||
InvokeVerifier.verify("methodWithArguments").with(1.0, InvokeMatcher.anyMapOf(Int::class.javaObjectType, Float::class.javaObjectType)).times(2)
|
||||
InvokeVerifier.verify("methodWithArguments").with(InvokeMatcher.anyList(), InvokeMatcher.anySetOf(Float::class.javaObjectType)).times(2)
|
||||
InvokeVerifier.verify("methodWithArguments").with(InvokeMatcher.anyList(), InvokeMatcher.anyListOf(Float::class.javaObjectType))
|
||||
InvokeVerifier.verify("methodWithArrayArgument").with(InvokeMatcher.anyArrayOf(Long::class.javaObjectType))
|
||||
InvokeVerifier.verify("methodWithArrayArgument").with(InvokeMatcher.anyArray())
|
||||
InvocationVerifier.verifyInvoked("methodWithArguments").with(1.0, InvocationMatcher.anyMapOf(Int::class.javaObjectType, Float::class.javaObjectType)).times(2)
|
||||
InvocationVerifier.verifyInvoked("methodWithArguments").with(InvocationMatcher.anyList(), InvocationMatcher.anySetOf(Float::class.javaObjectType)).times(2)
|
||||
InvocationVerifier.verifyInvoked("methodWithArguments").with(InvocationMatcher.anyList(), InvocationMatcher.anyListOf(Float::class.javaObjectType))
|
||||
InvocationVerifier.verifyInvoked("methodWithArrayArgument").with(InvocationMatcher.anyArrayOf(Long::class.javaObjectType))
|
||||
InvocationVerifier.verifyInvoked("methodWithArrayArgument").with(InvocationMatcher.anyArray())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun should_match_string_arguments() {
|
||||
demoMatcher.callMethodWithStringArgument()
|
||||
InvokeVerifier.verify("methodWithArguments").with(InvokeMatcher.startsWith("he"), InvokeMatcher.endsWith("ld"))
|
||||
InvokeVerifier.verify("methodWithArguments").with(InvokeMatcher.contains("stab"), InvokeMatcher.matches("m.[cd]k"))
|
||||
InvokeVerifier.verify("methodWithArrayArgument").with(InvokeMatcher.anyArrayOf(String::class.java))
|
||||
InvocationVerifier.verifyInvoked("methodWithArguments").with(InvocationMatcher.startsWith("he"), InvocationMatcher.endsWith("ld"))
|
||||
InvocationVerifier.verifyInvoked("methodWithArguments").with(InvocationMatcher.contains("stab"), InvocationMatcher.matches("m.[cd]k"))
|
||||
InvocationVerifier.verifyInvoked("methodWithArrayArgument").with(InvocationMatcher.anyArrayOf(String::class.java))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun should_match_object_arguments() {
|
||||
demoMatcher.callMethodWithObjectArgument()
|
||||
InvokeVerifier.verify("methodWithArguments").withInOrder(InvokeMatcher.any(BlackBox::class.java), InvokeMatcher.any(BlackBox::class.java))
|
||||
InvokeVerifier.verify("methodWithArguments").withInOrder(InvokeMatcher.nullable(BlackBox::class.java), InvokeMatcher.nullable(BlackBox::class.java))
|
||||
InvokeVerifier.verify("methodWithArguments").withInOrder(InvokeMatcher.isNull(), InvokeMatcher.notNull())
|
||||
InvocationVerifier.verifyInvoked("methodWithArguments").withInOrder(InvocationMatcher.any(BlackBox::class.java), InvocationMatcher.any(BlackBox::class.java))
|
||||
InvocationVerifier.verifyInvoked("methodWithArguments").withInOrder(InvocationMatcher.nullable(BlackBox::class.java), InvocationMatcher.nullable(BlackBox::class.java))
|
||||
InvocationVerifier.verifyInvoked("methodWithArguments").withInOrder(InvocationMatcher.isNull(), InvocationMatcher.notNull())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun should_match_with_times() {
|
||||
demoMatcher.callMethodWithNumberArguments()
|
||||
InvokeVerifier.verify("methodWithArguments").with(InvokeMatcher.anyNumber(), InvokeMatcher.any()).times(4)
|
||||
InvocationVerifier.verifyInvoked("methodWithArguments").with(InvocationMatcher.anyNumber(), InvocationMatcher.any()).times(4)
|
||||
|
||||
demoMatcher.callMethodWithNumberArguments()
|
||||
var gotError = false
|
||||
try {
|
||||
InvokeVerifier.verify("methodWithArguments").with(InvokeMatcher.anyNumber(), InvokeMatcher.any()).times(5)
|
||||
InvocationVerifier.verifyInvoked("methodWithArguments").with(InvocationMatcher.anyNumber(), InvocationMatcher.any()).times(5)
|
||||
} catch (e: VerifyFailedError) {
|
||||
gotError = true
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package com.alibaba.demo.basic
|
||||
|
||||
import com.alibaba.testable.core.annotation.MockConstructor
|
||||
import com.alibaba.testable.core.annotation.MockMethod
|
||||
import com.alibaba.testable.core.matcher.InvokeVerifier.verify
|
||||
import com.alibaba.testable.core.annotation.MockNew
|
||||
import com.alibaba.testable.core.annotation.MockInvoke
|
||||
import com.alibaba.testable.core.matcher.InvocationVerifier.verifyInvoked
|
||||
import com.alibaba.testable.core.tool.TestableTool.SOURCE_METHOD
|
||||
import com.alibaba.testable.core.tool.TestableTool.MOCK_CONTEXT
|
||||
import com.alibaba.demo.basic.model.mock.BlackBox
|
||||
@@ -20,37 +20,37 @@ internal class DemoMockTest {
|
||||
private val demoMock = DemoMock()
|
||||
|
||||
class Mock {
|
||||
@MockConstructor
|
||||
@MockNew
|
||||
private fun createBlackBox(text: String) = BlackBox("mock_$text")
|
||||
|
||||
@MockMethod(targetClass = DemoMock::class)
|
||||
@MockInvoke(targetClass = DemoMock::class)
|
||||
private fun innerFunc(text: String) = "mock_$text"
|
||||
|
||||
@MockMethod(targetClass = DemoMock::class)
|
||||
@MockInvoke(targetClass = DemoMock::class)
|
||||
private fun staticFunc(): String {
|
||||
return "_MOCK_TAIL";
|
||||
}
|
||||
|
||||
@MockMethod(targetClass = BlackBox::class)
|
||||
@MockInvoke(targetClass = BlackBox::class)
|
||||
private fun trim() = "trim_string"
|
||||
|
||||
@MockMethod(targetClass = BlackBox::class, targetMethod = "substring")
|
||||
@MockInvoke(targetClass = BlackBox::class, targetMethod = "substring")
|
||||
private fun sub(i: Int, j: Int) = "sub_string"
|
||||
|
||||
@MockMethod(targetClass = BlackBox::class)
|
||||
@MockInvoke(targetClass = BlackBox::class)
|
||||
private fun startsWith(s: String) = false
|
||||
|
||||
@MockMethod(targetClass = BlackBox::class)
|
||||
@MockInvoke(targetClass = BlackBox::class)
|
||||
private fun secretBox(): BlackBox {
|
||||
return BlackBox("not_secret_box")
|
||||
}
|
||||
|
||||
@MockMethod(targetClass = ColorBox::class)
|
||||
@MockInvoke(targetClass = ColorBox::class)
|
||||
private fun createBox(color: String, box: BlackBox): BlackBox {
|
||||
return BlackBox("White_${box.get()}")
|
||||
}
|
||||
|
||||
@MockMethod(targetClass = DemoMock::class)
|
||||
@MockInvoke(targetClass = DemoMock::class)
|
||||
private fun callFromDifferentMethod(): String {
|
||||
return if (MOCK_CONTEXT["case"] == "special_case") {
|
||||
"mock_special"
|
||||
@@ -66,35 +66,35 @@ internal class DemoMockTest {
|
||||
@Test
|
||||
fun should_mock_new_object() {
|
||||
assertEquals("mock_something", demoMock.newFunc())
|
||||
verify("createBlackBox").with("something")
|
||||
verifyInvoked("createBlackBox").with("something")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun should_mock_member_method() {
|
||||
assertEquals("{ \"res\": \"mock_hello_MOCK_TAIL\"}", demoMock.outerFunc("hello"))
|
||||
verify("innerFunc").with("hello")
|
||||
verify("staticFunc").with()
|
||||
verifyInvoked("innerFunc").with("hello")
|
||||
verifyInvoked("staticFunc").with()
|
||||
}
|
||||
|
||||
// @Test
|
||||
// fun should_mock_method_in_companion_object() {
|
||||
// assertEquals("CALL_MOCK_TAIL", DemoMock.callStaticFunc())
|
||||
// verify("staticFunc").with()
|
||||
// verifyInvoked("staticFunc").with()
|
||||
// }
|
||||
|
||||
@Test
|
||||
fun should_mock_common_method() {
|
||||
assertEquals("trim_string__sub_string__false", demoMock.commonFunc())
|
||||
verify("trim").withTimes(1)
|
||||
verify("sub").withTimes(1)
|
||||
verify("startsWith").withTimes(1)
|
||||
verifyInvoked("trim").withTimes(1)
|
||||
verifyInvoked("sub").withTimes(1)
|
||||
verifyInvoked("startsWith").withTimes(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun should_mock_static_method() {
|
||||
assertEquals("White_not_secret_box", demoMock.getBox().get())
|
||||
verify("secretBox").withTimes(1)
|
||||
verify("createBox").withTimes(1)
|
||||
verifyInvoked("secretBox").withTimes(1)
|
||||
verifyInvoked("createBox").withTimes(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -105,7 +105,7 @@ internal class DemoMockTest {
|
||||
assertEquals("mock_one_mock_others", Executors.newSingleThreadExecutor().submit<String> {
|
||||
demoMock.callerOne() + "_" + demoMock.callerTwo()
|
||||
}.get())
|
||||
verify("callFromDifferentMethod").withTimes(4)
|
||||
verifyInvoked("callFromDifferentMethod").withTimes(4)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -117,7 +117,7 @@ internal class DemoMockTest {
|
||||
assertEquals("mock_special", Executors.newSingleThreadExecutor().submit<String> {
|
||||
demoMock.callerOne()
|
||||
}.get())
|
||||
verify("callFromDifferentMethod").withTimes(2)
|
||||
verifyInvoked("callFromDifferentMethod").withTimes(2)
|
||||
MOCK_CONTEXT.clear()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,8 @@ import com.alibaba.demo.basic.model.omni.Child
|
||||
import com.alibaba.demo.basic.model.omni.Parent
|
||||
import com.alibaba.testable.core.tool.OmniAccessor
|
||||
import com.alibaba.testable.core.tool.OmniConstructor
|
||||
import org.junit.jupiter.api.Assertions
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertNotNull
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
/**
|
||||
@@ -18,14 +19,14 @@ internal class DemoOmniMethodsTest {
|
||||
val parent = OmniConstructor.newInstance(Parent::class.java)
|
||||
|
||||
// 任意深度的子孙成员对象都不为空
|
||||
Assertions.assertNotNull(parent.child?.grandChild?.content)
|
||||
assertNotNull(parent.child?.grandChild?.content)
|
||||
|
||||
// 所有基础类型初始化为默认数值
|
||||
Assertions.assertEquals(0, parent.child?.grandChild?.value)
|
||||
Assertions.assertEquals("", parent.child?.grandChild?.content)
|
||||
assertEquals(0, parent.child?.grandChild?.value)
|
||||
assertEquals("", parent.child?.grandChild?.content)
|
||||
|
||||
// 所有数组类型初始化为空数组
|
||||
Assertions.assertEquals(0, parent.children?.size)
|
||||
assertEquals(0, parent.children?.size)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -39,23 +40,23 @@ internal class DemoOmniMethodsTest {
|
||||
|
||||
// 使用成员名称快速读取成员对象
|
||||
var contents = OmniAccessor.get<String?>(parent, "content")
|
||||
Assertions.assertEquals(4, contents.size)
|
||||
Assertions.assertEquals("from child", contents[0])
|
||||
Assertions.assertEquals("from 1st children", contents[1])
|
||||
Assertions.assertEquals("from 2nd children", contents[2])
|
||||
Assertions.assertEquals("from 3rd children", contents[3])
|
||||
assertEquals(4, contents.size)
|
||||
assertEquals("from child", contents[0])
|
||||
assertEquals("from 1st children", contents[1])
|
||||
assertEquals("from 2nd children", contents[2])
|
||||
assertEquals("from 3rd children", contents[3])
|
||||
|
||||
// 使用成员类型快速读取成员对象
|
||||
contents = OmniAccessor.get(parent, "{Child}/{GrandChild}/content")
|
||||
Assertions.assertEquals(1, contents.size)
|
||||
Assertions.assertEquals("from child", contents[0])
|
||||
assertEquals(1, contents.size)
|
||||
assertEquals("from child", contents[0])
|
||||
|
||||
// 使用带下标的路径读取成员对象
|
||||
Assertions.assertEquals("from 2nd children", OmniAccessor.getFirst(parent, "children[1]/{GrandChild}/content"))
|
||||
Assertions.assertEquals("from 3rd children", OmniAccessor.getFirst(parent, "{Child[]}[2]/{GrandChild}/content"))
|
||||
assertEquals("from 2nd children", OmniAccessor.getFirst(parent, "children[1]/{GrandChild}/content"))
|
||||
assertEquals("from 3rd children", OmniAccessor.getFirst(parent, "{Child[]}[2]/{GrandChild}/content"))
|
||||
|
||||
// 使用模糊路径快速读取成员对象
|
||||
Assertions.assertEquals("from 1st children", OmniAccessor.getFirst(parent, "{C*[]}[0]/*/con*t"))
|
||||
assertEquals("from 1st children", OmniAccessor.getFirst(parent, "{C*[]}[0]/*/con*t"))
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -65,22 +66,22 @@ internal class DemoOmniMethodsTest {
|
||||
|
||||
// 使用指定路径快速给成员对象赋值
|
||||
OmniAccessor.set(parent, "child/grandChild/content", "demo child")
|
||||
Assertions.assertEquals("demo child", parent.child?.grandChild?.content)
|
||||
assertEquals("demo child", parent.child?.grandChild?.content)
|
||||
|
||||
// 使用带下标的路径给成员对象赋值
|
||||
OmniAccessor.set(parent, "children[1]/grandChild/content", "demo children[1]")
|
||||
Assertions.assertEquals("demo children[1]", parent.children?.get(1)?.grandChild?.content)
|
||||
assertEquals("demo children[1]", parent.children?.get(1)?.grandChild?.content)
|
||||
|
||||
// 使用模糊路径批量给成员对象赋值
|
||||
OmniAccessor.set(parent, "child*/*/content", "demo in batch")
|
||||
Assertions.assertEquals("demo in batch", parent.child?.grandChild?.content)
|
||||
Assertions.assertEquals("demo in batch", parent.children?.get(0)?.grandChild?.content)
|
||||
Assertions.assertEquals("demo in batch", parent.children?.get(1)?.grandChild?.content)
|
||||
Assertions.assertEquals("demo in batch", parent.children?.get(2)?.grandChild?.content)
|
||||
assertEquals("demo in batch", parent.child?.grandChild?.content)
|
||||
assertEquals("demo in batch", parent.children?.get(0)?.grandChild?.content)
|
||||
assertEquals("demo in batch", parent.children?.get(1)?.grandChild?.content)
|
||||
assertEquals("demo in batch", parent.children?.get(2)?.grandChild?.content)
|
||||
|
||||
// 读写私有内部类类型的成员(使用类型名引用内部类时,无需带外部类名)
|
||||
Assertions.assertEquals("", OmniAccessor.getFirst(parent, "subChild/secret"))
|
||||
assertEquals("", OmniAccessor.getFirst(parent, "subChild/secret"))
|
||||
OmniAccessor.set(parent, "{InnerChild}/secret", "inner-class secret")
|
||||
Assertions.assertEquals("inner-class secret", OmniAccessor.getFirst(parent, "subChild/secret"))
|
||||
assertEquals("inner-class secret", OmniAccessor.getFirst(parent, "subChild/secret"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.alibaba.demo.basic
|
||||
|
||||
import com.alibaba.testable.core.tool.PrivateAccessor
|
||||
import com.alibaba.testable.core.tool.PrivateAccessor.*
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
@@ -15,31 +15,31 @@ internal class DemoPrivateAccessorTest {
|
||||
@Test
|
||||
fun should_access_private_method() {
|
||||
val list = listOf("a", "b", "c");
|
||||
assertEquals("abc + hello + 1", PrivateAccessor.invoke(demoPrivateAccess, "privateFunc", list, "hello", 1))
|
||||
assertEquals("abc + hello + 1", invoke(demoPrivateAccess, "privateFunc", list, "hello", 1))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun should_access_private_field() {
|
||||
PrivateAccessor.set(demoPrivateAccess, "count", 3)
|
||||
assertEquals(3, PrivateAccessor.get(demoPrivateAccess, "count"))
|
||||
set(demoPrivateAccess, "count", 3)
|
||||
assertEquals(3, get(demoPrivateAccess, "count"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun should_access_private_static_method() {
|
||||
val list = listOf("a", "b", "c");
|
||||
assertEquals("hello + 1", PrivateAccessor.invokeStatic(DemoPrivateAccess::class.java, "privateStaticFunc", "hello", 1))
|
||||
assertEquals("abc * hello * 1", PrivateAccessor.invokeStatic(DemoPrivateAccess::class.java, "privateJvmStaticFunc", list, "hello", 1))
|
||||
assertEquals("hello + 1", invokeStatic(DemoPrivateAccess::class.java, "privateStaticFunc", "hello", 1))
|
||||
assertEquals("abc * hello * 1", invokeStatic(DemoPrivateAccess::class.java, "privateJvmStaticFunc", list, "hello", 1))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun should_access_private_static_field() {
|
||||
PrivateAccessor.setStatic(DemoPrivateAccess::class.java, "staticCount", 3)
|
||||
assertEquals(3, PrivateAccessor.getStatic(DemoPrivateAccess::class.java, "staticCount"))
|
||||
setStatic(DemoPrivateAccess::class.java, "staticCount", 3)
|
||||
assertEquals(3, getStatic(DemoPrivateAccess::class.java, "staticCount"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun should_update_final_field() {
|
||||
PrivateAccessor.set(demoPrivateAccess, "pi", 4.13)
|
||||
set(demoPrivateAccess, "pi", 4.13)
|
||||
assertEquals(4.13, demoPrivateAccess.pi)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
package com.alibaba.demo.basic
|
||||
|
||||
import com.alibaba.testable.core.annotation.MockConstructor
|
||||
import com.alibaba.testable.core.annotation.MockMethod
|
||||
import org.junit.jupiter.api.Assertions
|
||||
import com.alibaba.testable.core.annotation.MockNew
|
||||
import com.alibaba.testable.core.annotation.MockInvoke
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Test
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* 演示模板方法的Mock场景
|
||||
@@ -15,24 +14,24 @@ internal class DemoTemplateTest {
|
||||
private val demoTemplate = DemoTemplate()
|
||||
|
||||
class Mock {
|
||||
@MockMethod
|
||||
@MockInvoke
|
||||
private fun <T> getList(self: DemoTemplate, value: T): List<T> {
|
||||
return mutableListOf((value.toString() + "_mock_list") as T)
|
||||
}
|
||||
|
||||
@MockMethod
|
||||
@MockInvoke
|
||||
private fun <K, V> getMap(self: DemoTemplate, key: K, value: V): Map<K, V> {
|
||||
return mutableMapOf(key to (value.toString() + "_mock_map") as V)
|
||||
}
|
||||
|
||||
@MockConstructor
|
||||
@MockNew
|
||||
private fun newHashSet(): HashSet<*> {
|
||||
val set = HashSet<Any>()
|
||||
set.add("insert_mock")
|
||||
return set
|
||||
}
|
||||
|
||||
@MockMethod
|
||||
@MockInvoke
|
||||
private fun <E> add(s: MutableSet<E>, e: E): Boolean {
|
||||
s.add((e.toString() + "_mocked") as E)
|
||||
return true
|
||||
@@ -42,22 +41,22 @@ internal class DemoTemplateTest {
|
||||
@Test
|
||||
fun should_mock_single_template_method() {
|
||||
val res = demoTemplate.singleTemplateMethod()
|
||||
Assertions.assertEquals("demo_mock_list", res)
|
||||
assertEquals("demo_mock_list", res)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun should_mock_double_template_method() {
|
||||
val res = demoTemplate.doubleTemplateMethod()
|
||||
Assertions.assertEquals("testable_mock_map", res)
|
||||
assertEquals("testable_mock_map", res)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun should_mock_new_template_method() {
|
||||
val res = demoTemplate.newTemplateMethod()
|
||||
Assertions.assertEquals(2, res.size)
|
||||
assertEquals(2, res.size)
|
||||
val iterator = res.stream().iterator()
|
||||
Assertions.assertEquals("insert_mock", iterator.next())
|
||||
Assertions.assertEquals("world_mocked", iterator.next())
|
||||
assertEquals("insert_mock", iterator.next())
|
||||
assertEquals("world_mocked", iterator.next())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
package com.alibaba.demo.java2kotlin
|
||||
|
||||
import com.alibaba.testable.core.annotation.MockMethod
|
||||
import com.alibaba.testable.core.matcher.InvokeVerifier.verify
|
||||
import com.alibaba.testable.core.annotation.MockInvoke
|
||||
import com.alibaba.testable.core.matcher.InvocationVerifier.verifyInvoked
|
||||
import org.junit.jupiter.api.Test
|
||||
import java.io.File
|
||||
|
||||
class PathDemoTest {
|
||||
|
||||
class Mock {
|
||||
@MockMethod
|
||||
@MockInvoke
|
||||
fun exists(f: File): Boolean {
|
||||
return when (f.absolutePath) {
|
||||
"/a/b" -> true
|
||||
@@ -17,7 +17,7 @@ class PathDemoTest {
|
||||
}
|
||||
}
|
||||
|
||||
@MockMethod
|
||||
@MockInvoke
|
||||
fun isDirectory(f: File): Boolean {
|
||||
return when (f.absolutePath) {
|
||||
"/a/b/c" -> true
|
||||
@@ -25,12 +25,12 @@ class PathDemoTest {
|
||||
}
|
||||
}
|
||||
|
||||
@MockMethod
|
||||
@MockInvoke
|
||||
fun delete(f: File): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
@MockMethod
|
||||
@MockInvoke
|
||||
fun listFiles(f: File): Array<File>? {
|
||||
return when (f.absolutePath) {
|
||||
"/a/b" -> arrayOf(File("/a/b/c"), File("/a/b/d"))
|
||||
@@ -43,8 +43,8 @@ class PathDemoTest {
|
||||
@Test
|
||||
fun should_mock_java_method_invoke_in_kotlin() {
|
||||
PathDemo.deleteRecursively(File("/a/b/"))
|
||||
verify("listFiles").withTimes(2)
|
||||
verify("delete").withTimes(4)
|
||||
verifyInvoked("listFiles").withTimes(2)
|
||||
verifyInvoked("delete").withTimes(4)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package com.alibaba.demo.one2multi
|
||||
|
||||
import com.alibaba.testable.core.annotation.MockMethod
|
||||
import com.alibaba.testable.core.annotation.MockInvoke
|
||||
|
||||
class ASvcMock {
|
||||
|
||||
@MockMethod(targetClass = String::class, targetMethod = "format")
|
||||
@MockInvoke(targetClass = String::class, targetMethod = "format")
|
||||
fun a_format(format: String, vararg args: Any?): String {
|
||||
return "a_mock"
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package com.alibaba.demo.one2multi
|
||||
|
||||
import com.alibaba.testable.core.annotation.MockMethod
|
||||
import com.alibaba.testable.core.annotation.MockInvoke
|
||||
|
||||
class BSvcMock {
|
||||
|
||||
@MockMethod(targetClass = String::class, targetMethod = "format")
|
||||
@MockInvoke(targetClass = String::class, targetMethod = "format")
|
||||
fun b_format(format: String, vararg args: Any?): String {
|
||||
return "b_mock"
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package com.alibaba.demo.one2multi
|
||||
|
||||
import com.alibaba.testable.core.annotation.MockMethod
|
||||
import com.alibaba.testable.core.annotation.MockInvoke
|
||||
|
||||
class CSvcMock {
|
||||
|
||||
@MockMethod(targetClass = String::class, targetMethod = "format")
|
||||
@MockInvoke(targetClass = String::class, targetMethod = "format")
|
||||
fun c_format(format: String, vararg args: Any?): String {
|
||||
return "c_mock"
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package com.alibaba.demo.one2multi
|
||||
|
||||
import com.alibaba.testable.core.annotation.MockWith
|
||||
import com.alibaba.testable.core.matcher.InvokeVerifier.verify
|
||||
import org.junit.jupiter.api.Assertions
|
||||
import com.alibaba.testable.core.matcher.InvocationVerifier.verifyInvoked
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
@MockWith
|
||||
@@ -14,12 +14,12 @@ class OneToMultiSvcTest {
|
||||
|
||||
@Test
|
||||
fun should_test_multi_class_together() {
|
||||
Assertions.assertEquals("a_mock", aSvc.demo("test"))
|
||||
Assertions.assertEquals("b_mock", bSvc.demo("test"))
|
||||
Assertions.assertEquals("c_mock", cSvc.demo("test"))
|
||||
verify("a_format").withTimes(1)
|
||||
verify("b_format").withTimes(1)
|
||||
verify("c_format").withTimes(1)
|
||||
assertEquals("a_mock", aSvc.demo("test"))
|
||||
assertEquals("b_mock", bSvc.demo("test"))
|
||||
assertEquals("c_mock", cSvc.demo("test"))
|
||||
verifyInvoked("a_format").withTimes(1)
|
||||
verifyInvoked("b_format").withTimes(1)
|
||||
verifyInvoked("c_format").withTimes(1)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
omni.constructor.enhance.enable = true
|
||||
mock.target.checking.enable = true
|
||||
|
||||
@@ -14,8 +14,8 @@ repositories {
|
||||
dependencies {
|
||||
testImplementation 'org.codehaus.groovy:groovy-all:3.0.7'
|
||||
testImplementation 'org.spockframework:spock-core:2.0-M5-groovy-3.0'
|
||||
testImplementation('com.alibaba.testable:testable-all:0.6.4')
|
||||
testAnnotationProcessor('com.alibaba.testable:testable-processor:0.6.4')
|
||||
testImplementation('com.alibaba.testable:testable-all:0.6.10')
|
||||
testAnnotationProcessor('com.alibaba.testable:testable-processor:0.6.10')
|
||||
}
|
||||
|
||||
tasks.withType(JavaCompile) {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<java.version>1.8</java.version>
|
||||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
<testable.version>0.6.4</testable.version>
|
||||
<testable.version>0.6.10</testable.version>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package com.github.pbetkier.spockdemo
|
||||
|
||||
import com.alibaba.testable.core.annotation.MockConstructor
|
||||
import com.alibaba.testable.core.annotation.MockMethod
|
||||
import com.alibaba.testable.core.annotation.MockNew
|
||||
import com.alibaba.testable.core.annotation.MockInvoke
|
||||
import com.github.pbetkier.spockdemo.model.SpockBox
|
||||
import spock.lang.Shared
|
||||
import spock.lang.Specification
|
||||
|
||||
import static com.alibaba.testable.core.matcher.InvokeVerifier.verify;
|
||||
import static com.alibaba.testable.core.matcher.InvocationVerifier.verifyInvoked;
|
||||
|
||||
class DemoSpockTest extends Specification {
|
||||
|
||||
@@ -14,14 +14,14 @@ class DemoSpockTest extends Specification {
|
||||
def demoSpock = new DemoSpock()
|
||||
|
||||
static class Mock {
|
||||
@MockConstructor
|
||||
@MockNew
|
||||
SpockBox createBox() {
|
||||
SpockBox box = new SpockBox()
|
||||
box.put("mock zero")
|
||||
return box
|
||||
}
|
||||
|
||||
@MockMethod(targetMethod = "put")
|
||||
@MockInvoke(targetMethod = "put")
|
||||
void putBox(SpockBox self, String data) {
|
||||
self.put("mock " + data)
|
||||
}
|
||||
@@ -37,8 +37,8 @@ class DemoSpockTest extends Specification {
|
||||
box.pop() == "mock 2"
|
||||
box.pop() == "mock 1"
|
||||
box.pop() == "mock zero"
|
||||
verify("createBox").withTimes(1)
|
||||
verify("putBox").withInOrder("1").withInOrder("2").withInOrder("3")
|
||||
verifyInvoked("createBox").withTimes(1)
|
||||
verifyInvoked("putBox").withInOrder("1").withInOrder("2").withInOrder("3")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -10,4 +10,4 @@ However, when the current mainstream mock framework implements the mock function
|
||||
Therefore, we developed `TestableMock`, **a maverick and lightweight mock tool**.
|
||||
|
||||
|
||||

|
||||

|
||||
|
||||
@@ -19,7 +19,7 @@ Besides `TestableMock`, there are also several other community mock tools, such
|
||||
|
||||
`JMockit` is a mock tool whose functionality and convenience are between `Mockito` and `PowerMock`, and it makes up for their respective shortcomings. The project tried to launch a rewritten version of JMockit2 in 2017 but failed to complete, and is currently in an inactive maintenance state.
|
||||
|
||||
The functionality of `TestabledMock` is basically the same as that of `PowerMock`, and it is extremely easy to use. You can complete most tasks only by mastering the annotations of `@MockMethod`.
|
||||
The functionality of `TestabledMock` is basically the same as that of `PowerMock`, and it is extremely easy to use. You can complete most tasks only by mastering the annotations of `@MockInvoke`.
|
||||
|
||||
The main disadvantage of the current `TestableMock` is that the IDE cannot promptly prompt whether the method parameters are matched correctly when writing the mock method. If the mocking effect does not meet expectation, it has to be verified during runtime through the method provided in the [self-help troubleshooting](en-us/doc/troubleshooting.md) document. This feature needs to be provided by extending IDE plugins in the future.
|
||||
|
||||
|
||||
@@ -51,12 +51,14 @@ In actual scenarios, there are very few scenarios where methods in the `String`
|
||||
|
||||
#### 8. Can `TestableMock` be used for testing Android projects?
|
||||
|
||||
It can be used in combination with [Roboelectric](https://github.com/robolectric/robolectric) testing framework.
|
||||
Yes, check `demo/android-demo` project for more information.
|
||||
|
||||
The `Dalvik` and `ART` virtual machines of the Android system use a bytecode system different from the standard JVM, which will affect the normal functionality of `TestableMock`. The `Roboelectric` framework can run Android unit tests on a standard JVM virtual machine, which is much faster than running unit tests through the Android virtual machine. Recently, most Android App unit tests are written with the `Roboelectric` framework.
|
||||
Please note, the `Dalvik` and `ART` virtual machines of the Android system use a bytecode system different from the standard JVM, which will affect the normal functionality of `TestableMock`.
|
||||
If the test case require classes from Android SDK, it should be used in combination with [Roboelectric](https://github.com/robolectric/robolectric) testing framework.
|
||||
This framework will run Android unit tests on a standard JVM virtual machine, which is much faster than running unit tests through the Android virtual machine, thus many Android App unit tests are written with the `Roboelectric` framework.
|
||||
|
||||
#### 9. Meet "Command Line is too Long. Shorten command line for ..." error when triggering test in IntelliJ IDE?
|
||||
|
||||
This problem is caused by the system `Class Path` content is too long, and has nothing to do with `TestableMock`. However, it should be noted that IntelliJ provides two auxiliary solutions: `JAR manifest` and `classpath file`. If `TestableMock` is used in the test, please select `JAR manifest`.
|
||||
|
||||

|
||||

|
||||
|
||||
@@ -9,7 +9,7 @@ The **verifiers** and **matchers** are provided in `TestableMock` to achieve thi
|
||||
@Test
|
||||
public test_case() {
|
||||
int res = insToTest.methodToTest();
|
||||
verify("mockMethod").with(123, "abc");
|
||||
verifyInvoked("mockMethod").with(123, "abc");
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -1,5 +1,39 @@
|
||||
# Release Note
|
||||
|
||||
## 0.7.0
|
||||
- fix an improper exception issue when mock method with `associated` scope invoked
|
||||
- rename type `InvokeVerifier`/`InvockeMatcher` to `InvocationVerifier`/`InvocationMatcher`
|
||||
- rename method `verify` in `InvocationVerifier` type to `verifyInvoked`
|
||||
- rename annotation `@MockMethod`/`@MockConstructor` to `@MockInvoke`/`@MockNew`
|
||||
|
||||
## 0.6.10
|
||||
- support mock invocation via function reference
|
||||
- support mock method defined in a base interface
|
||||
|
||||
## 0.6.9
|
||||
- support mock invocation in lambda method
|
||||
- fix a `NullPointerException` issue when `PrivateAccessor.invoke()` has `null` parameter
|
||||
|
||||
## 0.6.8
|
||||
- support `@DumpTo` annotation to dump bytecode of any transformed class
|
||||
- `PrivateAccessor.setStatic()` method now able to update static final members
|
||||
|
||||
## 0.6.7
|
||||
- support empty mock container class which has all mock methods in its parent
|
||||
- fix an issue cause mock fail when parameter has array element accessing
|
||||
|
||||
## 0.6.6
|
||||
- support global package path mapping for easier mocking code of 3rd packages
|
||||
- complete mock target checking, improve resistance to code refactoring
|
||||
- fix a log disorder issue of mock class handler
|
||||
- fix an error of creating `Long` or `Integer` class with `OmniConstructor` in certain jvm version
|
||||
|
||||
## 0.6.5
|
||||
- fix an issue cause `OmniConstructor` fail to handle classes like Date and BigDecimal
|
||||
- fix an issue of `PrivateAccessor` fail to invoke method with single array parameter
|
||||
- fix an issue of mock methods in super class are ignored when `scope` is `associated`
|
||||
- fix an issue of empty mock method not works when `scope` is `associated`
|
||||
|
||||
## 0.6.4
|
||||
- `TestableNull` class removed, let `OmniConstructor` even lighter
|
||||
- support `@Nested` annotation of JUnit 5
|
||||
@@ -38,6 +72,13 @@
|
||||
- fix an exception caused by method parameter with ternary operator
|
||||
- fix a bug cause log message lost when `@MockWith` annotation used
|
||||
|
||||
## 0.5.0
|
||||
- split test class and mock class, let the mock class and method reusable
|
||||
- support use mock when package path of the test class is different from the class under test
|
||||
- support limit the mock effective scope to the cases of class under test it bound
|
||||
- use `TransmittableThreadLocal` to store mock context
|
||||
- add annotation `@MockDiagnose` to print debug logs
|
||||
|
||||
## 0.4.12
|
||||
- support verbose diagnose log for better self-troubleshooting
|
||||
- support disable private access target existence check
|
||||
|
||||
@@ -6,7 +6,7 @@ Use TestableMock
|
||||
- [Quickly mock arbitrary call](en-us/doc/use-mock.md): quickly replace any method invocation in the class under test with a mock method, solve the cumbersome use of traditional mock tools problem
|
||||
- [Access private members of the class under test](en-us/doc/private-accessor.md): enable unit tests directly invoke or access private members of the class under test, solve the problems of private member initialization and private method testing
|
||||
- [Quickly construct complicated parameter object](en-us/doc/omni-constructor.md):generate arbitrarily nested object instances, simplify their internal member assignment methods, solve the problem of long initialization codes for method parameters
|
||||
- [Auxiliary test void method](en-us/doc/test-void-method.md): use the mock validator to check the internal logic of method, solve the problem that unit testing is difficult to implement to the method with no return value
|
||||
- [Assist test void method](en-us/doc/test-void-method.md): use the mock validator to check the internal logic of method, solve the problem that unit testing is difficult to implement to the method with no return value
|
||||
|
||||
## Use in Maven project
|
||||
|
||||
@@ -16,7 +16,7 @@ It is recommended to add a `property` field that identifies the TestableMock ver
|
||||
|
||||
```xml
|
||||
<properties>
|
||||
<testable.version>0.6.4</testable.version>
|
||||
<testable.version>0.6.10</testable.version>
|
||||
</properties>
|
||||
```
|
||||
|
||||
@@ -63,8 +63,8 @@ Add dependence of `TestableMock` in `build.gradle` file:
|
||||
|
||||
```groovy
|
||||
dependencies {
|
||||
testImplementation('com.alibaba.testable:testable-all:0.6.4')
|
||||
testAnnotationProcessor('com.alibaba.testable:testable-processor:0.6.4')
|
||||
testImplementation('com.alibaba.testable:testable-all:0.6.10')
|
||||
testAnnotationProcessor('com.alibaba.testable:testable-processor:0.6.10')
|
||||
}
|
||||
```
|
||||
|
||||
@@ -78,7 +78,7 @@ test {
|
||||
|
||||
See the [build.gradle](https://github.com/alibaba/testable-mock/blob/master/demo/java-demo/build.gradle) file of project `java-demo` and the [build.gradle.kts](https://github.com/alibaba/testable-mock/blob/master/demo/kotlin-demo/build.gradle.kts) file of project `kotlin-demo`.
|
||||
|
||||
> For Android project tested with `Robolectric` framework, please use the same method to add `TestableMock` dependency as above, and add `javaagent` configuration as follows:
|
||||
> For Android project, please use the same method to add `TestableMock` dependency as above, and add `javaagent` configuration as follows:
|
||||
>
|
||||
> ```groovy
|
||||
> android {
|
||||
@@ -92,13 +92,14 @@ See the [build.gradle](https://github.com/alibaba/testable-mock/blob/master/demo
|
||||
> }
|
||||
> ```
|
||||
>
|
||||
> See [issue-43](https://github.com/alibaba/testable-mock/issues/43) for a complete example.
|
||||
> See project `demo/android-demo` for a complete example.
|
||||
|
||||
> If the project is using `Spock` test framework, you need to specify the bytecode version generated by `Groovy` to be 1.6 or above, the method is as follows (please modify the properties value according to the actual JVM version used).
|
||||
>
|
||||
> For Maven project, add `<maven.compiler.source>` and `<maven.compiler.target>` properties inside the `pom.xml` file, e.g.
|
||||
> ```xml
|
||||
> <properties>
|
||||
> <!-- or 1.7/1.8/... -->
|
||||
> <maven.compiler.source>1.6</maven.compiler.source>
|
||||
> <maven.compiler.target>1.6</maven.compiler.target>
|
||||
> </properties>
|
||||
@@ -106,7 +107,7 @@ See the [build.gradle](https://github.com/alibaba/testable-mock/blob/master/demo
|
||||
>
|
||||
> For Gradle project, add a `sourceCompatibility` property inside the `build.gradle` file, e.g.
|
||||
> ```groovy
|
||||
> sourceCompatibility = '6'
|
||||
> sourceCompatibility = '6' // or 7/8/9/...
|
||||
> ```
|
||||
>
|
||||
> See project `demo/spock-demo` for a complete example.
|
||||
|
||||
@@ -70,7 +70,7 @@ class Demo {
|
||||
|
||||
To test this method, you can use `TestableMock` to quickly mock out the `System.out.println` method. In the mock method body, you can simply call the original method (equivalent to not affecting the original method function, only used for call recording), or leave it blank (equivalent to removing the side effects of the original method).
|
||||
|
||||
After executing the void type method under test, use `InvokeVerifier.verify()` to verify whether the incoming print content meets expectations:
|
||||
After executing the void type method under test, use `InvocationVerifier.verifyInvoked()` to verify whether the incoming print content meets expectations:
|
||||
|
||||
```java
|
||||
class DemoTest {
|
||||
@@ -78,7 +78,7 @@ class DemoTest {
|
||||
|
||||
public static class Mock {
|
||||
// Intercept `System.out.println` invocation
|
||||
@MockMethod
|
||||
@MockInvoke
|
||||
public void println(PrintStream ps, String msg) {
|
||||
// Execute the original call
|
||||
ps.println(msg);
|
||||
@@ -90,7 +90,7 @@ class DemoTest {
|
||||
Action action = new Action("click", ":download");
|
||||
demo.recordAction();
|
||||
// Verify mock method `println` is invoked, and passing parameters in line with expectations
|
||||
verify("println").with(matches("\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2} \\[click\\] :download"));
|
||||
verifyInvoked("println").with(matches("\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2} \\[click\\] :download"));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -39,7 +39,7 @@ public class DemoMockTest {
|
||||
@Test
|
||||
void should_mock_member_method() throws Exception {
|
||||
assertEquals("hello_world", demoMock.outerFunc());
|
||||
verify("innerFunc").with("world");
|
||||
verifyInvoked("innerFunc").with("world");
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -59,7 +59,7 @@ public class DemoMockTest {
|
||||
@Test
|
||||
void should_mock_member_method() throws Exception {
|
||||
assertEquals("hello_world", demoMock.outerFunc());
|
||||
verify("innerFunc").with("world");
|
||||
verifyInvoked("innerFunc").with("world");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -15,4 +15,4 @@ At the same time, because the built-in unit test executor of `Eclipse` completel
|
||||
|
||||
Take the use of `JUnit` as an example. You need to pull down from the small triangle next to the run button on the IDE toolbar, select "Run Configurations...", select the task to run the unit test on the left side, and switch to "arguments" Tab on the right side, append a `-javaagent:` parameter in the "VM Options", the following figure is an example, note that the `testable-agent` package should be modified to match the actual situation of the local Maven repository path.
|
||||
|
||||

|
||||

|
||||
|
||||