Compare commits

..

2 Commits

Author SHA1 Message Date
金戟
02ba7ad83c follow up to 0.4.11 2021-02-06 21:11:48 +08:00
金戟
064926e952 use junit 4 in java demo 2021-01-12 19:46:23 +08:00
329 changed files with 2706 additions and 13523 deletions

1
.gitignore vendored
View File

@@ -1,6 +1,5 @@
# maven ignore
target/
.mvn/
# gradle ignore
build/

View File

@@ -2,22 +2,27 @@
换种思路写Mock让单元测试更简单。
无需初始化,不挑服务框架甭管要换的是私有方法、静态方法、构造方法还是其他任何类的任何方法也甭管要换的对象是怎么创建的。写好Mock定义加个`@MockInvoke`注解,一切统统搞定。
无需初始化,不挑测试框架甭管要换的是私有方法、静态方法、构造方法还是其他任何类的任何方法也甭管要换的对象是怎么创建的。写好Mock定义加个`@MockMethod`注解,一切统统搞定。
- 文档https://alibaba.github.io/testable-mock/
- 国内文档镜像http://freyrlin.gitee.io/testable-mock/
阅读[这里](https://mp.weixin.qq.com/s/KyU6Eu7mDkZU8FspfSqfMw)了解更多故事。
<font size="5">**0.7版本已发布**</font>,从`0.6.x`升级到`0.7.x`版本请参考[版本升级指南](https://alibaba.github.io/testable-mock/#/zh-cn/doc/upgrade-guide)
> 特别说明
> 1. 如遇到"Attempt to access non-static member in mock method"错误,参见[常见问题](https://alibaba.github.io/testable-mock/#/zh-cn/doc/frequently-asked-questions)第8条
> 2. 如果有遇到其他任何使用问题和建议,请直接在[Issue](https://github.com/alibaba/testable-mock/issues)中提出,也可通过[Pull Request](https://github.com/alibaba/testable-mock/pulls)提交您的代码我们将在24小时内回复并处理
-----
## 项目维护说明
## 版本计划
由于当前并行项目较多,此项目暂时转入维护阶段,在此期间`TestableMock`会继续提供不定期的版本更新。
`TestableMock`正在持续迭代演进,以下版本计划可能在开发过程中发生调整
如果有遇到其他任何使用问题和建议,请直接在[Issues](https://github.com/alibaba/testable-mock/issues)中提出,也可通过[Pull Request](https://github.com/alibaba/testable-mock/pulls)提交您的代码,我们将尽快回复并处理。
- `0.4` 当前版本,进行中的工作内容参考[Issue](https://github.com/alibaba/testable-mock/issues)清单
- `0.5` 实现以"独立Mock类"为单元的Mock方法复用机制让测试类之间可以方便复用相同的Mock方法
- `0.6` 实现将Mock方法的默认生效范围缩小至当前被测类避免Mock方法在测试类之间相互影响
- `1.0` 功能稳定,一个崭新的开始
## 目录结构
@@ -28,12 +33,9 @@
|-- testable-agent ➜ JavaAgent模块提供Mock测试相关功能
|-- testable-core ➜ 基础功能模块提供Mock相关注解和工具类
|-- testable-maven-plugin ➜ Maven插件模块用于简化JavaAgent注入
|-- tool ➜ 项目开发过程中的工具脚本
|-- demo
| |-- java-demo ➜ Java语言的示例代码
| |-- kotlin-demo ➜ Kotlin语言的示例代码
| |-- android-demo ➜ Android项目的示例代码
| `-- spock-demo ➜ Spock测试框架的示例代码
| `-- kotlin-demo ➜ Kotlin语言的示例代码
`-- docs ➜ 项目使用文档
```

View File

@@ -3,13 +3,18 @@
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 `@MockInvoke` annotation, everything is done.
Write a mock method, add an `@MockMethod` annotation, everything is done.
Usage Document: https://alibaba.github.io/testable-mock/#/en-us/
## Roadmap
## Loadmap
`TestableMock` is still under heavy development, if you get sucked somehow, just go ahead raise an [issue](https://github.com/alibaba/testable-mock/issues) for it.
`TestableMock` is still under heavy development, the following version plans may be adjusted during the iteration
- `v0.4` it's the current version, refer to the [issue](https://github.com/alibaba/testable-mock/issues) list for the work in progress
- `v0.5` implementation mock class inherit mechanism, so that the same mock method can be reused between test classes conveniently
- `v0.6` narrows the default effective scope of mock methods to the class under test, to avoid interaction of mocking cross classes
- `v1.0` all functions are stable, a brand-new start
## Directory Structure
@@ -20,12 +25,9 @@ Usage Document: https://alibaba.github.io/testable-mock/#/en-us/
|-- testable-agent ➜ JavaAgent module, provides test mocking related functions
|-- testable-core ➜ Basic function module, provides mock related class and annotation
|-- testable-maven-plugin ➜ Maven plugin module, for simplify JavaAgent injection
|-- tool ➜ Scripts for project maintain
|-- demo
| |-- java-demo ➜ Java code example
| |-- kotlin-demo ➜ Kotlin code example
| |-- android-demo ➜ Android app code example
| `-- spock-demo ➜ Spock framwork code example
| `-- kotlin-demo ➜ Kotlin code example
`-- docs ➜ Source code of usage document
```

View File

@@ -1,15 +0,0 @@
*.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

View File

@@ -1 +0,0 @@
/build

View File

@@ -1,55 +0,0 @@
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.7.9'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}

View File

@@ -1,21 +0,0 @@
# 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

View File

@@ -1,24 +0,0 @@
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)
}
}

View File

@@ -1,13 +0,0 @@
<?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>

View File

@@ -1,63 +0,0 @@
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";
}
}

View File

@@ -1,32 +0,0 @@
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;
}
}

View File

@@ -1,30 +0,0 @@
<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>

View File

@@ -1,170 +0,0 @@
<?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>

View File

@@ -1,5 +0,0 @@
<?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>

View File

@@ -1,5 +0,0 @@
<?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>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -1,16 +0,0 @@
<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>

View File

@@ -1,10 +0,0 @@
<?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>

View File

@@ -1,3 +0,0 @@
<resources>
<string name="app_name">Testable Android Demo</string>
</resources>

View File

@@ -1,16 +0,0 @@
<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>

View File

@@ -1,122 +0,0 @@
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);
}
}

View File

@@ -1,47 +0,0 @@
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.");
}
}

View File

@@ -1 +0,0 @@
log.file = null

View File

@@ -1,28 +0,0 @@
// 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
}

View File

@@ -1,21 +0,0 @@
# 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

View File

@@ -1,6 +0,0 @@
#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

View File

@@ -1,172 +0,0 @@
#!/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" "$@"

View File

@@ -1,84 +0,0 @@
@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

View File

@@ -1,2 +0,0 @@
include ':app'
rootProject.name = "Testable Android Demo"

View File

@@ -12,9 +12,9 @@ repositories {
}
dependencies {
testImplementation('org.junit.jupiter:junit-jupiter:5.6.2')
testImplementation('com.alibaba.testable:testable-all:0.7.9')
testAnnotationProcessor('com.alibaba.testable:testable-processor:0.7.9')
testImplementation('junit:junit:4.13.1')
testImplementation('com.alibaba.testable:testable-all:0.4.11')
testAnnotationProcessor('com.alibaba.testable:testable-processor:0.4.11')
}
tasks.withType(JavaCompile) {
@@ -23,5 +23,5 @@ tasks.withType(JavaCompile) {
test {
jvmArgs "-javaagent:${classpath.find { it.name.contains("testable-agent") }.absolutePath}"
useJUnitPlatform()
useJUnit()
}

View File

@@ -11,8 +11,8 @@
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<junit.version>5.9.1</junit.version>
<testable.version>0.7.9</testable.version>
<junit.version>4.13.1</junit.version>
<testable.version>0.4.11</testable.version>
</properties>
<dependencies>
@@ -23,8 +23,8 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
@@ -32,10 +32,11 @@
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M7</version>
<version>3.0.0-M5</version>
<configuration>
<argLine>@{argLine} -javaagent:${settings.localRepository}/com/alibaba/testable/testable-agent/${testable.version}/testable-agent-${testable.version}.jar</argLine>
</configuration>
@@ -56,7 +57,7 @@
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.8</version>
<version>0.8.6</version>
<executions>
<execution>
<id>prepare-agent</id>

View File

@@ -1,33 +0,0 @@
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();
}
}

View File

@@ -1,18 +0,0 @@
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();
}
}

View File

@@ -1,60 +0,0 @@
package com.alibaba.demo.basic;
import java.util.concurrent.*;
/**
* 演示对内部类的Mock支持
* Demonstrate support for mocking invocation inside a inner class
*/
public class DemoInnerClass {
public static class StaticInner {
/**
* invocation inside a static inner class
*/
public String demo() {
return methodToBeMock();
}
}
public class Inner {
/**
* invocation inside a non-static inner class
*/
public String demo() {
return methodToBeMock();
}
}
private ExecutorService executorService = Executors.newSingleThreadExecutor();
public String callAnonymousInner() throws ExecutionException, InterruptedException {
/**
* invocation inside a anonymous inner class
*/
Future<String> future = executorService.submit(new Callable<String>() {
@Override
public String call() throws Exception {
return methodToBeMock();
}
});
return future.get();
}
public String callLambdaInner() throws ExecutionException, InterruptedException {
/**
* invocation inside a lambda inner class
*/
Future<String> future = executorService.submit(() -> methodToBeMock());
return future.get();
}
public String callInnerDemo() {
return new Inner().demo();
}
public static String methodToBeMock() {
return "RealCall";
}
}

View File

@@ -1,7 +0,0 @@
package com.alibaba.demo.basic.model.mock;
public interface BasicColor {
String getColorIndex();
}

View File

@@ -1,27 +0,0 @@
package com.alibaba.demo.basic.model.mock;
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";
}
@Override
public String getColorIndex() {
return "idx";
}
}

View File

@@ -1,13 +0,0 @@
package com.alibaba.demo.basic.model.mock;
abstract public class Box {
protected String data;
abstract public void put(String something);
public String get() {
return data;
}
}

View File

@@ -1,7 +0,0 @@
package com.alibaba.demo.basic.model.mock;
public interface Color extends BasicColor {
String getColor();
}

View File

@@ -1,47 +0,0 @@
package com.alibaba.demo.basic.model.omni;
public class Child {
/**
* 我是一个私有的构造方法
* This class have only private constructor
*/
private Child() {
}
// ---------- 内部成员字段 ----------
private GrandChild grandChild;
private InnerChild subChild;
/**
* 这是一个私有内部类
* An private inner class
*/
private class InnerChild {
private String secret;
}
/* ---------------------------------------
以下Getters/Setters方法仅为便于功能演示而添加
并非OmniConstructor或OmniAccessor功能所需
--------------------------------------- */
public GrandChild getGrandChild() {
return grandChild;
}
public void setGrandChild(GrandChild grandChild) {
this.grandChild = grandChild;
}
public InnerChild getSubChild() {
return subChild;
}
public void setSubChild(InnerChild subChild) {
this.subChild = subChild;
}
}

View File

@@ -1,31 +0,0 @@
package com.alibaba.demo.basic.model.omni;
public class GrandChild {
// ---------- 内部成员字段 ----------
private int value;
private String content;
/* ---------------------------------------
以下Getters/Setters方法仅为便于功能演示而添加
并非OmniConstructor或OmniAccessor功能所需
--------------------------------------- */
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}

View File

@@ -1,40 +0,0 @@
package com.alibaba.demo.basic.model.omni;
public class Parent {
/**
* 我是一个虽然存在但无法正常使用的构造方法
* This class have constructor with exception throw
*/
public Parent() {
throw new IllegalArgumentException();
}
// ---------- 内部成员字段 ----------
private Child child;
private Child[] children;
/* ---------------------------------------
以下Getters/Setters方法仅为便于功能演示而添加
并非OmniConstructor或OmniAccessor功能所需
--------------------------------------- */
public Child getChild() {
return child;
}
public void setChild(Child child) {
this.child = child;
}
public Child[] getChildren() {
return children;
}
public void setChildren(Child[] children) {
this.children = children;
}
}

View File

@@ -1,25 +0,0 @@
package com.alibaba.demo.inherit;
/**
* 演示使用@MockContainer注解实现Mock容器类的多重继承
* Demonstrate multiple inherit of mock container class with @MockContainer annotation
*/
public class DemoMultipleInherit {
private String prefix() {
return "ori_";
}
private String middle() {
return "gin";
}
private String suffix(int some, String more, Object[] parameters) {
return "_al";
}
public String entry() {
return prefix() + middle() + suffix(0, null, null);
}
}

View File

@@ -1,21 +0,0 @@
package com.alibaba.demo.inherit;
/**
* 演示使用extends关键字实现Mock容器类的继承
* Demonstrate inherit of mock container class
*/
public class DemoSingleInherit {
private String prefix() {
return "re_";
}
private String suffix() {
return "_al";
}
public String entry(String word) {
return prefix() + word + suffix();
}
}

View File

@@ -1,42 +0,0 @@
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 jim
*/
public interface BaseDemo {
default void blackHole(Object... ignore) {}
default void consumesRun(Runnable r) {
r.run();
}
default void consumesFunction0(Runnable f) {
f.run();
}
default <T> void consumesFunction1(Consumer<T> f) {
f.accept(null);
}
default <T, R> void consumesFunction2(Function<T, R> f) {
f.apply(null);
}
default <T1, T2, R> void consumesFunction3(BiFunction<T1, T2, R> f) {
f.apply(null, null);
}
default <T> void consumesSupplier(Supplier<T> supplier) {
supplier.get();
}
default <T, R> void consumesTwoFunction2(Function<T, R> f1, Function<T, R> f2) {
f1.apply(null);
f2.apply(null);
}
}

View File

@@ -1,121 +0,0 @@
package com.alibaba.demo.lambda;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* @author jim
*/
@SuppressWarnings("SimplifyStreamApiCallChains")
public class CollectionListCodeDemo implements BaseDemo {
public void list() {
List<User> userList = getUsers();
double d = userList.stream()
.map(v -> v)
.map(v -> Function.<User>identity().apply(v))
.map(User::getId)
.mapToDouble(Double::valueOf)
.reduce(0, Double::sum);
//.reduce(0, (v1, v2) -> v1 + v2);
blackHole(d);
blackHole(userList.stream()
.map(User::getId)
.mapToLong(Long::valueOf)
.reduce(0, Long::sum));
blackHole(userList.stream()
.map(User::getId)
.mapToInt(Long::intValue)
.reduce(0, Integer::sum));
Map<Long, User> map = userList.stream()
.filter(User::isActive)
.collect(Collectors.toMap(User::getId, Function.identity(), (v1, v2) -> v1));
blackHole(map);
List<User> l1 = userList.stream()
.collect(Collectors.toList())
.stream()
.collect(Collectors.toSet())
.stream()
.collect(Collectors.groupingBy(User::getName))
.entrySet()
.stream()
.filter(v -> v.getValue().stream().anyMatch(User::isActive))
.map(Map.Entry::getValue)
.flatMap(Collection::parallelStream)
.collect(Collectors.groupingBy(User::getId))
.values()
.stream()
.flatMap(List::stream)
.collect(Collectors.toList());
blackHole(l1);
userList.stream()
.map(User::isActive)
.reduce(Boolean::logicalAnd)
.ifPresent(this::blackHole);
}
private List<User> getUsers() {
List<User> userList = new ArrayList<>();
for (int i=0; i<10; i++) {
userList.add(getUser(i));
}
return userList;
}
private User getUser(int index) {
User u1 = new User();
u1.id = (long)index;
u1.name = "u" + index;
u1.age = index;
u1.active = true;
return u1;
}
private static class User {
private Long id;
private String name;
private int age;
private boolean active;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public boolean isActive() {
return active;
}
public void setActive(boolean active) {
this.active = active;
}
}
}

View File

@@ -1,9 +0,0 @@
package com.alibaba.demo.lambda;
/**
* @author jim
*/
@FunctionalInterface
public interface Function1Throwable<T, R> {
R apply(T t) throws Throwable;
}

View File

@@ -1,86 +0,0 @@
package com.alibaba.demo.lambda;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
/**
* @author jim
*/
public class InvokeInterfaceDemo implements BaseDemo {
public void collectionInterfaceDefaultOrStatic() {
blackHole(collectionStreamTest());
blackHole(arraysStreamTest());
}
public void interfaceDefault() {
ILambda l = new LambdaFoo();
consumesRun(l::run);
consumesFunction1(l::function1);
}
public void interfaceStatic() {
consumesRun(ILambda::staticRun);
consumesFunction1(ILambda::staticFunction1);
}
public Object collectionStreamTest() {
List<List<String>> testList = new ArrayList<>();
List<String> fooList = new ArrayList<>();
fooList.add("123");
fooList.add("456");
testList.add(fooList);
return testList.stream()
//.flatMap(v -> v.stream())
.flatMap(Collection::stream)
.map(Double::valueOf)
.map(BigDecimal::new)
.reduce(BigDecimal::add);
}
public Object arraysStreamTest() {
List<String[]> zz = new ArrayList<>();
zz.add(new String[]{"1", "2", "3"});
return zz.stream()
.flatMap(Arrays::stream)
.map(Double::valueOf)
.map(BigDecimal::new)
.reduce(BigDecimal::add);
}
public void objectStaticMethodReference() {
List<Boolean> f = new ArrayList<>();
f.add(false);
f.add(false);
f.add(false);
blackHole(f.stream()
.reduce(Boolean::logicalAnd)
.get()
);
}
public interface ILambda {
default void run() {
}
default void function1(String s) {
}
static void staticRun() {
}
static void staticFunction1(String s) {
}
}
public static class LambdaFoo implements ILambda {
}
}

View File

@@ -1,243 +0,0 @@
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 InvokeVirtualDemo {
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() {
InvokeVirtualDemo invokeVirtualDemo = new InvokeVirtualDemo();
consumesFunction3(invokeVirtualDemo::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 "";
}
}
}

View File

@@ -1,218 +0,0 @@
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);
}
}

View File

@@ -1,33 +0,0 @@
package com.alibaba.demo.lambda;
/**
* @author jim
*/
public class StaticInstanceReference implements BaseDemo {
private static final StaticClassA STATIC_CLASS_A = new StaticClassA();
public void staticMethodReference() {
consumesRun(STATIC_CLASS_A::doIt);
consumesFunction1(STATIC_CLASS_A::function1);
consumesFunction2(STATIC_CLASS_A::function2);
consumesFunction3(STATIC_CLASS_A::function3);
}
public static class StaticClassA {
public void doIt() {
}
public void function1(String s) {
}
public Integer function2(String s) {
return 1;
}
public Integer function3(String s, Double d) {
return 1;
}
}
}

View File

@@ -1,15 +0,0 @@
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;
}
}

View File

@@ -1,9 +0,0 @@
package com.alibaba.demo.one2multi;
public class ASvc {
public String demo(String name) {
return String.format("a_%s", name);
}
}

View File

@@ -1,9 +0,0 @@
package com.alibaba.demo.one2multi;
public class BSvc {
public String demo(String name) {
return String.format("b_%s", name);
}
}

View File

@@ -1,9 +0,0 @@
package com.alibaba.demo.one2multi;
public class CSvc {
public String demo(String name) {
return String.format("c_%s", name);
}
}

View File

@@ -1,17 +1,17 @@
package com.alibaba.demo.basic;
package com.alibaba.testable.demo;
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.demo.model.BlackBox;
import com.alibaba.testable.demo.model.Box;
import com.alibaba.testable.demo.model.Color;
/**
* 演示父类变量引用子类对象时的Mock场景
* Demonstrate scenario of mocking method from sub-type object referred by parent-type variable
*/
public class DemoReference {
public class DemoInherit {
/**
* call method overridden by subclass via parent class variable
* call method overridden by sub class via parent class variable
*/
public Box putIntoBox() {
Box box = new BlackBox("");
@@ -20,7 +20,7 @@ public class DemoReference {
}
/**
* call method overridden by subclass via subclass variable
* call method overridden by sub class via sub class variable
*/
public BlackBox putIntoBlackBox() {
BlackBox box = new BlackBox("");
@@ -37,7 +37,7 @@ public class DemoReference {
}
/**
* call method defined in parent class via subclass variable
* call method defined in parent class via sub class variable
*/
public String getFromBlackBox() {
BlackBox box = new BlackBox("data");
@@ -53,18 +53,10 @@ public class DemoReference {
}
/**
* call method defined in interface via subclass variable
* call method defined in interface via sub class 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();
}
}

View File

@@ -1,7 +1,7 @@
package com.alibaba.demo.basic;
package com.alibaba.testable.demo;
import com.alibaba.demo.basic.model.mock.BlackBox;
import com.alibaba.testable.demo.model.BlackBox;
import java.util.ArrayList;
import java.util.HashMap;

View File

@@ -1,6 +1,6 @@
package com.alibaba.demo.basic;
package com.alibaba.testable.demo;
import com.alibaba.demo.basic.model.mock.BlackBox;
import com.alibaba.testable.demo.model.BlackBox;
import java.nio.file.Files;
import java.nio.file.Paths;

View File

@@ -1,4 +1,4 @@
package com.alibaba.demo.basic;
package com.alibaba.testable.demo;
import java.util.List;
@@ -46,9 +46,7 @@ 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 == null ? "null" : str)
+ " + " + i;
return list.stream().reduce((a, s) -> a + s).orElse("") + " + " + str + " + " + i;
}
}

View File

@@ -1,4 +1,4 @@
package com.alibaba.demo.basic;
package com.alibaba.testable.demo;
import java.util.*;

View File

@@ -1,27 +0,0 @@
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";
}
}

View File

@@ -1,21 +0,0 @@
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());
}
}

View File

@@ -1,29 +0,0 @@
package com.alibaba.demo.basic;
import com.alibaba.testable.core.annotation.MockInvoke;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
/**
* 演示对内部类的Mock支持
* Demonstrate support for mocking invocation inside a inner class
*/
class DemoInnerClassTest {
public static class Mock {
@MockInvoke(targetClass = DemoInnerClass.class)
String methodToBeMock() {
return "MockedCall";
}
}
@Test
void should_mock_invoke_inside_inner_class() throws Exception {
DemoInnerClass demo = new DemoInnerClass();
assertEquals("MockedCall", demo.callInnerDemo());
assertEquals("MockedCall", demo.callAnonymousInner());
assertEquals("MockedCall", demo.callLambdaInner());
assertEquals("MockedCall", new DemoInnerClass.StaticInner().demo());
}
}

View File

@@ -1,85 +0,0 @@
package com.alibaba.demo.basic;
import com.alibaba.demo.basic.model.mock.BlackBox;
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.InvocationMatcher.*;
import static com.alibaba.testable.core.matcher.InvocationVerifier.verifyInvoked;
import static org.junit.jupiter.api.Assertions.fail;
/**
* 演示Mock方法调用校验器
* Demonstrate mock method invocation verifier
*/
class DemoMatcherTest {
private DemoMatcher demoMatcher = new DemoMatcher();
public static class Mock {
@MockInvoke(targetMethod = "methodToBeMocked")
private void methodWithoutArgument(DemoMatcher self) {}
@MockInvoke(targetMethod = "methodToBeMocked")
private void methodWithArguments(DemoMatcher self, Object a1, Object a2) {}
@MockInvoke(targetMethod = "methodToBeMocked")
private void methodWithArrayArgument(DemoMatcher self, Object[] a) {}
}
@Test
void should_match_no_argument() {
demoMatcher.callMethodWithoutArgument();
verifyInvoked("methodWithoutArgument").withTimes(1);
demoMatcher.callMethodWithoutArgument();
verifyInvoked("methodWithoutArgument").withTimes(2);
}
@Test
void should_match_number_arguments() {
demoMatcher.callMethodWithNumberArguments();
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();
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();
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();
verifyInvoked("methodWithArguments").with(anyNumber(), any()).times(3);
demoMatcher.callMethodWithNumberArguments();
boolean gotError = false;
try {
verifyInvoked("methodWithArguments").with(anyNumber(), any()).times(4);
} catch (VerifyFailedError e) {
gotError = true;
}
if (!gotError) {
fail();
}
}
}

View File

@@ -1,120 +0,0 @@
package com.alibaba.demo.basic;
import com.alibaba.demo.basic.model.mock.BlackBox;
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.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;
/**
* 演示基本的Mock功能
* Demonstrate basic mock functionality
*/
class DemoMockTest {
private DemoMock demoMock = new DemoMock();
public static class Mock {
@MockNew
private BlackBox createBlackBox(String text) {
return new BlackBox("mock_" + text);
}
@MockInvoke(targetClass = DemoMock.class)
private String innerFunc(String text) {
return "mock_" + text;
}
@MockInvoke(targetClass = DemoMock.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 = DemoMock.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
void should_mock_new_object() {
assertEquals("mock_something", demoMock.newFunc());
verifyInvoked("createBlackBox").with("something");
}
@Test
void should_mock_member_method() throws Exception {
assertEquals("{ \"res\": \"mock_hello_MOCK_TAIL\"}", demoMock.outerFunc("hello"));
verifyInvoked("innerFunc").with("hello");
verifyInvoked("staticFunc").with();
}
@Test
void should_mock_common_method() {
assertEquals("trim_string__sub_string__false", demoMock.commonFunc());
verifyInvoked("trim").withTimes(1);
verifyInvoked("sub").withTimes(1);
verifyInvoked("startsWith").withTimes(1);
}
@Test
void should_mock_static_method() {
assertEquals("not_secret_box", demoMock.getBox().get());
verifyInvoked("secretBox").withTimes(1);
}
@Test
void should_get_source_method_name() throws Exception {
// synchronous
assertEquals("mock_one_mock_others", demoMock.callerOne() + "_" + demoMock.callerTwo());
// asynchronous
assertEquals("mock_one_mock_others",
Executors.newSingleThreadExecutor().submit(() -> demoMock.callerOne() + "_" + demoMock.callerTwo()).get());
verifyInvoked("callFromDifferentMethod").withTimes(4);
}
@Test
void should_set_mock_context() throws Exception {
MOCK_CONTEXT.put("case", "special_case");
// synchronous
assertEquals("mock_special", demoMock.callerOne());
// asynchronous
assertEquals("mock_special", Executors.newSingleThreadExecutor().submit(() -> demoMock.callerOne()).get());
verifyInvoked("callFromDifferentMethod").withTimes(2);
}
}

View File

@@ -1,91 +0,0 @@
package com.alibaba.demo.basic;
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.Test;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
/**
* 演示快速创建任意对象和使用路径访问成员
* Demonstrate quick object construction and access members by path
*/
class DemoOmniMethodsTest {
@Test
void should_construct_any_class() {
Parent parent = OmniConstructor.newInstance(Parent.class);
// 任意深度的子孙成员对象都不为空
assertNotNull(parent.getChild().getGrandChild().getContent());
// 所有基础类型初始化为默认数值
assertEquals(0, parent.getChild().getGrandChild().getValue());
assertEquals("", parent.getChild().getGrandChild().getContent());
// 所有数组类型初始化为空数组
assertEquals(0, parent.getChildren().length);
}
@Test
void should_get_any_member() {
Parent parent = OmniConstructor.newInstance(Parent.class);
parent.setChildren(OmniConstructor.newArray(Child.class, 3));
parent.getChild().getGrandChild().setContent("from child");
parent.getChildren()[0].getGrandChild().setContent("from 1st children");
parent.getChildren()[1].getGrandChild().setContent("from 2nd children");
parent.getChildren()[2].getGrandChild().setContent("from 3rd children");
// 使用成员名称快速读取成员对象
List<String> contents = OmniAccessor.get(parent, "content");
assertEquals(4, contents.size());
assertEquals("from child", contents.get(0));
assertEquals("from 1st children", contents.get(1));
assertEquals("from 2nd children", contents.get(2));
assertEquals("from 3rd children", contents.get(3));
// 使用成员类型快速读取成员对象
contents = OmniAccessor.get(parent, "{Child}/{GrandChild}/content");
assertEquals(1, contents.size());
assertEquals("from child", contents.get(0));
// 使用带下标的路径读取成员对象
assertEquals("from 2nd children", OmniAccessor.getFirst(parent, "children[1]/{GrandChild}/content"));
assertEquals("from 3rd children", OmniAccessor.getFirst(parent, "{Child[]}[2]/{GrandChild}/content"));
// 使用模糊路径快速读取成员对象
assertEquals("from 1st children", OmniAccessor.getFirst(parent, "{C*[]}[0]/*/con*t"));
}
@Test
void should_set_any_member() {
Parent parent = OmniConstructor.newInstance(Parent.class);
parent.setChildren(OmniConstructor.newArray(Child.class, 3));
// 使用指定路径快速给成员对象赋值
OmniAccessor.set(parent, "child/grandChild/content", "demo child");
assertEquals("demo child", parent.getChild().getGrandChild().getContent());
// 使用带下标的路径给成员对象赋值
OmniAccessor.set(parent, "children[1]/grandChild/content", "demo children[1]");
assertEquals("demo children[1]", parent.getChildren()[1].getGrandChild().getContent());
// 使用模糊路径批量给成员对象赋值
OmniAccessor.set(parent, "child*/*/content", "demo in batch");
assertEquals("demo in batch", parent.getChild().getGrandChild().getContent());
assertEquals("demo in batch", parent.getChildren()[0].getGrandChild().getContent());
assertEquals("demo in batch", parent.getChildren()[1].getGrandChild().getContent());
assertEquals("demo in batch", parent.getChildren()[2].getGrandChild().getContent());
// 读写私有内部类类型的成员(使用类型名引用内部类时,无需带外部类名)
assertEquals("", OmniAccessor.getFirst(parent, "subChild/secret"));
OmniAccessor.set(parent, "{InnerChild}/secret", "inner-class secret");
assertEquals("inner-class secret", OmniAccessor.getFirst(parent, "subChild/secret"));
}
}

View File

@@ -1,64 +0,0 @@
package com.alibaba.demo.basic;
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;
/**
* 演示使用`PrivateAccessor`工具类访问私有成员
* Demonstrate access private member via `PrivateAccessor` class
*/
class DemoPrivateAccessorTest {
private DemoPrivateAccess demoPrivateAccess = new DemoPrivateAccess();
@Test
void should_access_private_method() {
List<String> list = new ArrayList<String>() {{ add("a"); add("b"); add("c"); }};
assertEquals("member", invoke(demoPrivateAccess, "privateFunc"));
assertEquals("abc + hello + 1", invoke(demoPrivateAccess, "privateFuncWithArgs", list, "hello", 1));
}
@Test
void should_access_private_field() {
set(demoPrivateAccess, "count", 3);
assertEquals(Integer.valueOf(3), get(demoPrivateAccess, "count"));
}
@Test
void should_access_private_static_method() {
assertEquals("static", invokeStatic(DemoPrivateAccess.class, "privateStaticFunc"));
assertEquals("hello + 1", invokeStatic(DemoPrivateAccess.class, "privateStaticFuncWithArgs", "hello", 1));
}
@Test
void should_access_private_static_field() {
setStatic(DemoPrivateAccess.class, "staticCount", 3);
assertEquals(Integer.valueOf(3), getStatic(DemoPrivateAccess.class, "staticCount"));
}
@Test
void should_update_final_field() {
set(demoPrivateAccess, "pi", 3.14);
assertEquals(Double.valueOf(3.14), get(demoPrivateAccess, "pi"));
}
@Test
void should_use_null_parameter() {
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);
}
}

View File

@@ -1,59 +0,0 @@
package com.alibaba.demo.basic;
import com.alibaba.testable.processor.annotation.EnablePrivateAccess;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
/**
* 演示使用`@EnablePrivateAccess`注解访问私有成员
* Demonstrate access private member via `@EnablePrivateAccess` annotation
*/
@EnablePrivateAccess(srcClass = DemoPrivateAccess.class)
class DemoPrivateProcessorTest {
private DemoPrivateAccess demoPrivateAccess = new DemoPrivateAccess();
@Test
void should_access_private_method() {
List<String> list = new ArrayList<String>() {{ add("a"); add("b"); add("c"); }};
assertEquals("member", demoPrivateAccess.privateFunc());
assertEquals("abc + hello + 1", demoPrivateAccess.privateFuncWithArgs(list, "hello", 1));
}
@Test
void should_access_private_field() {
demoPrivateAccess.count = 2;
assertEquals(Integer.valueOf(2), demoPrivateAccess.count);
}
@Test
void should_access_private_static_method() {
assertEquals("static", DemoPrivateAccess.privateStaticFunc());
assertEquals("hello + 1", DemoPrivateAccess.privateStaticFuncWithArgs("hello", 1));
}
@Test
void should_access_private_static_field() {
DemoPrivateAccess.staticCount = 2;
assertEquals(Integer.valueOf(2), DemoPrivateAccess.staticCount);
}
@Test
void should_update_final_field() {
demoPrivateAccess.pi = 4.13;
assertEquals(Double.valueOf(4.13), demoPrivateAccess.pi);
}
@Test
void should_use_null_parameter() {
demoPrivateAccess.pi = null;
assertNull(demoPrivateAccess.pi);
assertEquals("null + 1", DemoPrivateAccess.privateStaticFuncWithArgs(null, 1));
}
}

View File

@@ -1,115 +0,0 @@
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.MockInvoke;
import org.junit.jupiter.api.Test;
import static com.alibaba.testable.core.matcher.InvocationVerifier.verifyInvoked;
import static org.junit.jupiter.api.Assertions.assertEquals;
/**
* 演示父类变量引用子类对象时的Mock场景
* Demonstrate scenario of mocking method from sub-type object referred by parent-type variable
*/
class DemoReferenceTest {
private DemoReference demoReference = new DemoReference();
public static class Mock {
/**
* 当需要引用原对象时,不加`targetClass`参数将原类型作为Mock方法的首个参数建议将参数命名为`self`
* When method body need to refer the invoked object, skip the use `targetClass` parameter,
* and add a parameter `self` with target class type before other paramter of the target method
*/
@MockInvoke(targetMethod = "put")
private void put_into_box(Box self, String something) {
self.put("put_" + something + "_into_box");
}
@MockInvoke(targetMethod = "put")
private void put_into_blackbox(BlackBox self, String something) {
self.put("put_" + something + "_into_blackbox");
}
/**
* 当不需要引用原对象时,将目标类型作为`@MockInvoke`注解的`targetClass`参数值即可
* When the invoked object is not used in the mock method, set `targetClass` value to the type who owns the mocked method
*/
@MockInvoke(targetClass = Box.class, targetMethod = "get")
private String get_from_box() {
return "get_from_box";
}
@MockInvoke(targetClass = BlackBox.class, targetMethod = "get")
private String get_from_blackbox() {
return "get_from_blackbox";
}
@MockInvoke(targetClass = Color.class, targetMethod = "getColor")
private String get_color_from_color() {
return "color_from_color";
}
@MockInvoke(targetClass = BlackBox.class, targetMethod = "getColor")
private String get_color_from_blackbox() {
return "color_from_blackbox";
}
@MockInvoke(targetClass = Color.class, targetMethod = "getColorIndex")
private String get_colorIdx_from_color() {
return "colorIdx_from_color";
}
}
@Test
void should_mock_call_sub_object_method_by_parent_object() {
BlackBox box = (BlackBox) demoReference.putIntoBox();
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 = demoReference.putIntoBlackBox();
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 = demoReference.getFromBox();
verifyInvoked("get_from_box").withTimes(1);
assertEquals("get_from_box", content);
}
@Test
void should_mock_call_parent_object_method_by_sub_object() {
String content = demoReference.getFromBlackBox();
verifyInvoked("get_from_blackbox").withTimes(1);
assertEquals("get_from_blackbox", content);
}
@Test
void should_mock_call_interface_method_by_interface_object() {
String color = demoReference.getColorViaColor();
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 = demoReference.getColorViaBox();
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 = demoReference.getColorIdxViaColor();
verifyInvoked("get_colorIdx_from_color").withTimes(1);
assertEquals("colorIdx_from_color", colorIdx);
}
}

View File

@@ -1,94 +0,0 @@
package com.alibaba.demo.basic;
import com.alibaba.testable.core.annotation.MockNew;
import com.alibaba.testable.core.annotation.MockInvoke;
import org.junit.jupiter.api.Test;
import java.util.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
/**
* 演示模板方法的Mock场景
* Demonstrate scenario of mocking template method
*/
class DemoTemplateTest {
private DemoTemplate demoTemplate = new DemoTemplate();
public static class Mock {
/* 第一种写法:使用泛型定义 */
/* First solution: use generics type */
@MockInvoke
private <T> List<T> getList(DemoTemplate self, T value) {
return new ArrayList<T>() {{ add((T)(value.toString() + "_mock_list")); }};
}
@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")); }};
}
@MockNew
private <T> HashSet<T> newHashSet() {
HashSet<T> set = new HashSet<>();
set.add((T)"insert_mock");
return set;
}
@MockInvoke
private <E> boolean add(Set s, E e) {
s.add(e.toString() + "_mocked");
return true;
}
/* 第二种写法使用Object类型 */
/* Second solution: use object type */
//@MockInvoke
//private List<Object> getList(DemoTemplate self, Object value) {
// return new ArrayList<Object>() {{ add(value.toString() + "_mock_list"); }};
//}
//
//@MockInvoke
//private Map<Object, Object> getMap(DemoTemplate self, Object key, Object value) {
// return new HashMap<Object, Object>() {{ put(key, value.toString() + "_mock_map"); }};
//}
//
//@MockNew
//private HashSet newHashSet() {
// HashSet<Object> set = new HashSet<>();
// set.add("insert_mock");
// return set;
//}
//
//@MockInvoke
//private boolean add(Set s, Object e) {
// s.add(e.toString() + "_mocked");
// return true;
//}
}
@Test
void should_mock_single_template_method() {
String res = demoTemplate.singleTemplateMethod();
assertEquals("demo_mock_list", res);
}
@Test
void should_mock_double_template_method() {
String res = demoTemplate.doubleTemplateMethod();
assertEquals("testable_mock_map", res);
}
@Test
void should_mock_new_template_method() {
Set<?> res = demoTemplate.newTemplateMethod();
assertEquals(2, res.size());
Iterator<?> iterator = res.stream().iterator();
assertEquals("insert_mock", iterator.next());
assertEquals("world_mocked", iterator.next());
}
}

View File

@@ -1,48 +0,0 @@
package com.alibaba.demo.inherit;
import com.alibaba.testable.core.annotation.MockContainer;
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.*;
/**
* 演示使用@MockContainer注解实现Mock容器类的多重继承
* Demonstrate multiple inherit of mock container class with @MockContainer annotation
*/
class DemoMultipleInheritTest {
private DemoMultipleInherit demoMultipleInherit = new DemoMultipleInherit();
public static class PrefixMock {
@MockInvoke(targetClass = DemoMultipleInherit.class)
private String prefix() {
return "in_";
}
}
public static class SuffixMock {
@MockInvoke(targetClass = DemoMultipleInherit.class)
private String suffix(int some, String more, Object[] parameters) {
return "_it";
}
}
@MockContainer(inherits = { PrefixMock.class, SuffixMock.class })
public static class Mock {
@MockInvoke(targetClass = DemoMultipleInherit.class)
private String middle() {
return "her";
}
}
@Test
public void should_use_mock_method_in_parent_class() {
assertEquals("in_her_it", demoMultipleInherit.entry());
verifyInvoked("prefix").withTimes(1);
verifyInvoked("middle").withTimes(1);
verifyInvoked("suffix").withTimes(1);
}
}

View File

@@ -1,38 +0,0 @@
package com.alibaba.demo.inherit;
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;
/**
* 演示Mock容器类的继承
* Demonstrate inherit of mock container class with extends keyword
*/
class DemoSingleInheritTest {
private DemoSingleInherit demoSingleInherit = new DemoSingleInherit();
public static class BasicMock {
@MockInvoke(targetClass = DemoSingleInherit.class)
private String suffix() {
return "_ck";
}
}
public static class Mock extends BasicMock {
@MockInvoke(targetClass = DemoSingleInherit.class)
private String prefix() {
return "mo_";
}
}
@Test
public void should_use_mock_method_in_parent_class() {
assertEquals("mo_test_ck", demoSingleInherit.entry("test"));
verifyInvoked("prefix").withTimes(1);
verifyInvoked("suffix").withTimes(1);
}
}

View File

@@ -1,25 +0,0 @@
package com.alibaba.demo.lambda;
import com.alibaba.testable.core.annotation.MockInvoke;
import org.junit.jupiter.api.Test;
/**
* @author jim
*/
public class CollectionListCodeDemoTest {
private final CollectionListCodeDemo instance = new CollectionListCodeDemo();
public static class Mock {
@MockInvoke(targetClass = String.class, targetMethod = "contains")
public boolean mockContains(CharSequence s) {
return false;
}
}
@Test
public void listTest() {
instance.list();
}
}

View File

@@ -1,69 +0,0 @@
package com.alibaba.demo.lambda;
import com.alibaba.testable.core.annotation.MockInvoke;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.stream.Stream;
import static com.alibaba.testable.core.matcher.InvocationVerifier.verifyInvoked;
/**
* @author jim
*/
public class InvokeInterfaceDemoTest {
private final InvokeInterfaceDemo instance = new InvokeInterfaceDemo();
public static class Mock {
@MockInvoke(targetClass = InvokeInterfaceDemo.ILambda.class, targetMethod = "run")
private void mockILambdaRun() {
}
@MockInvoke(targetClass = InvokeInterfaceDemo.ILambda.class, targetMethod = "function1")
private void mockILambdaFunction1(String s) {
}
@SuppressWarnings("unchecked")
@MockInvoke(targetClass = Collection.class, targetMethod = "stream")
<E> Stream<E> mockStream() {
List<E> fooList = new ArrayList<>();
fooList.add((E) "123");
fooList.add((E) "456");
return fooList.stream();
}
@MockInvoke(targetClass = Boolean.class, targetMethod = "logicalAnd")
public static boolean mockLogicalAnd(boolean a, boolean b) {
return false;
}
}
@Test
public void shouldMockCollectionStream() {
instance.collectionInterfaceDefaultOrStatic();
verifyInvoked("mockStream").withTimes(1);
}
@Test
public void shouldMockInterfaceDefault() {
instance.interfaceDefault();
verifyInvoked("mockILambdaRun").withTimes(1);
verifyInvoked("mockILambdaFunction1").withTimes(1);
}
@Test
public void shouldMockObjectStaticMethodReference() {
instance.objectStaticMethodReference();
verifyInvoked("mockLogicalAnd").withTimes(2);
}
@Test
public void shouldMockInterfaceStatic() {
instance.interfaceStatic();
}
}

View File

@@ -1,80 +0,0 @@
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 InvokeVirtualDemoTest {
private final InvokeVirtualDemo lambdaDemo = new InvokeVirtualDemo();
@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 = InvokeVirtualDemo.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);
}
}

View File

@@ -1,86 +0,0 @@
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");
}
}

View File

@@ -1,33 +0,0 @@
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 jim
*/
public class StaticInstanceReferenceTest {
private final StaticInstanceReference instance = new StaticInstanceReference();
public static class Mock {
@MockInvoke(targetClass = StaticInstanceReference.StaticClassA.class, targetMethod = "doIt")
private void mockDoIt() {
}
@MockInvoke(targetClass = StaticInstanceReference.StaticClassA.class)
private Integer function2(String s) {
return 2;
}
}
@Test
public void shouldMockDoIt() {
instance.staticMethodReference();
verifyInvoked("mockDoIt").withTimes(1);
verifyInvoked("function2").withTimes(1);
}
}

View File

@@ -1,12 +0,0 @@
package com.alibaba.demo.one2multi;
import com.alibaba.testable.core.annotation.MockInvoke;
public class ASvcMock {
@MockInvoke(targetClass = String.class, targetMethod = "format")
public String a_format(String format, Object... args) {
return "a_mock";
}
}

View File

@@ -1,12 +0,0 @@
package com.alibaba.demo.one2multi;
import com.alibaba.testable.core.annotation.MockInvoke;
public class BSvcMock {
@MockInvoke(targetClass = String.class, targetMethod = "format")
public String b_format(String format, Object... args) {
return "b_mock";
}
}

View File

@@ -1,12 +0,0 @@
package com.alibaba.demo.one2multi;
import com.alibaba.testable.core.annotation.MockInvoke;
public class CSvcMock {
@MockInvoke(targetClass = String.class, targetMethod = "format")
public String c_format(String format, Object... args) {
return "c_mock";
}
}

View File

@@ -1,27 +0,0 @@
package com.alibaba.demo.one2multi;
import com.alibaba.testable.core.annotation.MockWith;
import org.junit.jupiter.api.Test;
import static com.alibaba.testable.core.matcher.InvocationVerifier.verifyInvoked;
import static org.junit.jupiter.api.Assertions.assertEquals;
@MockWith
public class OneToMultiSvcTest {
private ASvc aSvc = new ASvc();
private BSvc bSvc = new BSvc();
private CSvc cSvc = new CSvc();
@Test
public void should_test_multi_class_together() {
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);
}
}

View File

@@ -0,0 +1,93 @@
package com.alibaba.testable.demo;
import com.alibaba.testable.core.annotation.MockMethod;
import com.alibaba.testable.demo.model.BlackBox;
import com.alibaba.testable.demo.model.Box;
import com.alibaba.testable.demo.model.Color;
import org.junit.Test;
import static com.alibaba.testable.core.matcher.InvokeVerifier.verify;
import static org.junit.Assert.assertEquals;
/**
* 演示父类变量引用子类对象时的Mock场景
* Demonstrate scenario of mocking method from sub-type object referred by parent-type variable
*/
public class DemoInheritTest {
private DemoInherit demoInherit = new DemoInherit();
@MockMethod(targetMethod = "put")
private void put_into_box(Box self, String something) {
self.put("put_" + something + "_into_box");
}
@MockMethod(targetMethod = "put")
private void put_into_blackbox(BlackBox self, String something) {
self.put("put_" + something + "_into_blackbox");
}
@MockMethod(targetMethod = "get")
private String get_from_box(Box self) {
return "get_from_box";
}
@MockMethod(targetMethod = "get")
private String get_from_blackbox(BlackBox self) {
return "get_from_blackbox";
}
@MockMethod(targetMethod = "getColor")
private String get_color_from_color(Color self) {
return "color_from_color";
}
@MockMethod(targetMethod = "getColor")
private String get_color_from_blackbox(BlackBox self) {
return "color_from_blackbox";
}
@Test
public void should_able_to_mock_call_sub_object_method_by_parent_object() {
BlackBox box = (BlackBox)demoInherit.putIntoBox();
verify("put_into_box").withTimes(1);
assertEquals("put_data_into_box", box.get());
}
@Test
public void should_able_to_mock_call_sub_object_method_by_sub_object() {
BlackBox box = demoInherit.putIntoBlackBox();
verify("put_into_blackbox").withTimes(1);
assertEquals("put_data_into_blackbox", box.get());
}
@Test
public void should_able_to_mock_call_parent_object_method_by_parent_object() {
String content = demoInherit.getFromBox();
verify("get_from_box").withTimes(1);
assertEquals("get_from_box", content);
}
@Test
public void should_able_to_mock_call_parent_object_method_by_sub_object() {
String content = demoInherit.getFromBlackBox();
verify("get_from_blackbox").withTimes(1);
assertEquals("get_from_blackbox", content);
}
@Test
public void should_able_to_mock_call_interface_method_by_interface_object() {
String color = demoInherit.getColorViaColor();
verify("get_color_from_color").withTimes(1);
assertEquals("color_from_color", color);
}
@Test
public void should_able_to_mock_call_interface_method_by_sub_class_object() {
String color = demoInherit.getColorViaBox();
verify("get_color_from_blackbox").withTimes(1);
assertEquals("color_from_blackbox", color);
}
}

View File

@@ -0,0 +1,84 @@
package com.alibaba.testable.demo;
import com.alibaba.testable.core.annotation.MockMethod;
import com.alibaba.testable.core.error.VerifyFailedError;
import com.alibaba.testable.demo.model.BlackBox;
import org.junit.Test;
import static com.alibaba.testable.core.matcher.InvokeMatcher.*;
import static com.alibaba.testable.core.matcher.InvokeVerifier.verify;
import static org.junit.Assert.fail;
/**
* 演示Mock方法调用校验器
* Demonstrate mock method invocation verifier
*/
public class DemoMatcherTest {
private DemoMatcher demoMatcher = new DemoMatcher();
@MockMethod(targetMethod = "methodToBeMocked")
private void methodWithoutArgument(DemoMatcher self) {}
@MockMethod(targetMethod = "methodToBeMocked")
private void methodWithArguments(DemoMatcher self, Object a1, Object a2) {}
@MockMethod(targetMethod = "methodToBeMocked")
private void methodWithArrayArgument(DemoMatcher self, Object[] a) {}
@Test
public void should_match_no_argument() {
demoMatcher.callMethodWithoutArgument();
verify("methodWithoutArgument").withTimes(1);
demoMatcher.callMethodWithoutArgument();
verify("methodWithoutArgument").withTimes(2);
}
@Test
public 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());
}
@Test
public 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));
}
@Test
public 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());
}
@Test
public void should_match_with_times() {
demoMatcher.callMethodWithNumberArguments();
verify("methodWithArguments").with(anyNumber(), any()).times(3);
demoMatcher.callMethodWithNumberArguments();
boolean gotError = false;
try {
verify("methodWithArguments").with(anyNumber(), any()).times(4);
} catch (VerifyFailedError e) {
gotError = true;
}
if (!gotError) {
fail();
}
}
}

View File

@@ -0,0 +1,118 @@
package com.alibaba.testable.demo;
import com.alibaba.testable.core.annotation.MockConstructor;
import com.alibaba.testable.core.annotation.MockMethod;
import com.alibaba.testable.demo.model.BlackBox;
import org.junit.Test;
import java.util.concurrent.Executors;
import static com.alibaba.testable.core.matcher.InvokeVerifier.verify;
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 DemoMockTest {
private DemoMock demoMock = new DemoMock();
@MockConstructor
private BlackBox createBlackBox(String text) {
return new BlackBox("mock_" + text);
}
@MockMethod(targetClass = DemoMock.class)
private String innerFunc(String text) {
return "mock_" + text;
}
@MockMethod(targetClass = DemoMock.class)
private String staticFunc() {
return "_MOCK_TAIL";
}
@MockMethod(targetClass = String.class)
private String trim() {
return "trim_string";
}
@MockMethod(targetClass = String.class, targetMethod = "substring")
private String sub(int i, int j) {
return "sub_string";
}
@MockMethod(targetClass = String.class)
private boolean startsWith(String s) {
return false;
}
@MockMethod(targetClass = BlackBox.class)
private BlackBox secretBox() {
return new BlackBox("not_secret_box");
}
@MockMethod(targetClass = DemoMock.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_able_to_mock_new_object() {
assertEquals("mock_something", demoMock.newFunc());
verify("createBlackBox").with("something");
}
@Test
public void should_able_to_mock_member_method() throws Exception {
assertEquals("{ \"res\": \"mock_hello_MOCK_TAIL\"}", demoMock.outerFunc("hello"));
verify("innerFunc").with("hello");
verify("staticFunc").with();
}
@Test
public void should_able_to_mock_common_method() {
assertEquals("trim_string__sub_string__false", demoMock.commonFunc());
verify("trim").withTimes(1);
verify("sub").withTimes(1);
verify("startsWith").withTimes(1);
}
@Test
public void should_able_to_mock_static_method() {
assertEquals("not_secret_box", demoMock.getBox().get());
verify("secretBox").withTimes(1);
}
@Test
public void should_able_to_get_source_method_name() throws Exception {
// synchronous
assertEquals("mock_one_mock_others", demoMock.callerOne() + "_" + demoMock.callerTwo());
// asynchronous
assertEquals("mock_one_mock_others",
Executors.newSingleThreadExecutor().submit(() -> demoMock.callerOne() + "_" + demoMock.callerTwo()).get());
verify("callFromDifferentMethod").withTimes(4);
}
@Test
public void should_able_to_get_test_case_name() throws Exception {
MOCK_CONTEXT.put("case", "special_case");
// synchronous
assertEquals("mock_special", demoMock.callerOne());
// asynchronous
assertEquals("mock_special", Executors.newSingleThreadExecutor().submit(() -> demoMock.callerOne()).get());
verify("callFromDifferentMethod").withTimes(2);
MOCK_CONTEXT.clear();
}
}

View File

@@ -0,0 +1,77 @@
package com.alibaba.testable.demo;
import com.alibaba.testable.core.accessor.PrivateAccessor;
import com.alibaba.testable.processor.annotation.EnablePrivateAccess;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
/**
* 演示私有成员访问功能
* Demonstrate private member access functionality
*/
@EnablePrivateAccess
public class DemoPrivateAccessTest {
private DemoPrivateAccess demoPrivateAccess = new DemoPrivateAccess();
@Test
public void should_able_to_access_private_method() {
List<String> list = new ArrayList<String>() {{ add("a"); add("b"); add("c"); }};
assertEquals("member", demoPrivateAccess.privateFunc());
assertEquals("member", PrivateAccessor.invoke(demoPrivateAccess, "privateFunc"));
assertEquals("abc + hello + 1", demoPrivateAccess.privateFuncWithArgs(list, "hello", 1));
assertEquals("abc + hello + 1", PrivateAccessor.invoke(demoPrivateAccess, "privateFuncWithArgs", list, "hello", 1));
}
@Test
public void should_able_to_access_private_field() {
demoPrivateAccess.count = 2;
assertEquals(Integer.valueOf(2), demoPrivateAccess.count);
PrivateAccessor.set(demoPrivateAccess, "count", 3);
assertEquals(Integer.valueOf(3), PrivateAccessor.get(demoPrivateAccess, "count"));
}
@Test
public void should_able_to_access_private_static_method() {
assertEquals("static", DemoPrivateAccess.privateStaticFunc());
assertEquals("static", PrivateAccessor.invokeStatic(DemoPrivateAccess.class, "privateStaticFunc"));
assertEquals("hello + 1", DemoPrivateAccess.privateStaticFuncWithArgs("hello", 1));
assertEquals("hello + 1", PrivateAccessor.invokeStatic(DemoPrivateAccess.class, "privateStaticFuncWithArgs", "hello", 1));
}
@Test
public void should_able_to_access_private_static_field() {
DemoPrivateAccess.staticCount = 2;
assertEquals(Integer.valueOf(2), DemoPrivateAccess.staticCount);
PrivateAccessor.setStatic(DemoPrivateAccess.class, "staticCount", 3);
assertEquals(Integer.valueOf(3), PrivateAccessor.getStatic(DemoPrivateAccess.class, "staticCount"));
}
@Test
public void should_able_to_update_final_field() {
demoPrivateAccess.pi = 4.13;
assertEquals(Double.valueOf(4.13), demoPrivateAccess.pi);
PrivateAccessor.set(demoPrivateAccess, "pi", 3.14);
assertEquals(Double.valueOf(3.14), PrivateAccessor.get(demoPrivateAccess, "pi"));
}
@Test
public void should_able_to_use_null_parameter() {
demoPrivateAccess.pi = null;
assertNull(demoPrivateAccess.pi);
assertEquals("null + 1", DemoPrivateAccess.privateStaticFuncWithArgs(null, 1));
PrivateAccessor.set(demoPrivateAccess, "pi", null);
assertNull(PrivateAccessor.get(demoPrivateAccess, "pi"));
assertEquals("null + 1", PrivateAccessor.invokeStatic(DemoPrivateAccess.class, "privateStaticFuncWithArgs", null, 1));
}
}

View File

@@ -0,0 +1,93 @@
package com.alibaba.testable.demo;
import com.alibaba.testable.core.annotation.MockConstructor;
import com.alibaba.testable.core.annotation.MockMethod;
import org.junit.Test;
import java.util.*;
import static org.junit.Assert.assertEquals;
/**
* 演示模板方法的Mock场景
* Demonstrate scenario of mocking template method
*/
public class DemoTemplateTest {
private DemoTemplate demoTemplate = new DemoTemplate();
/* 第一种写法:使用泛型定义 */
/* First solution: use generics type */
@MockMethod
private static <T> List<T> getList(DemoTemplate self, T value) {
return new ArrayList<T>() {{ add((T)(value.toString() + "_mock_list")); }};
}
@MockMethod
private static <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
private <T> HashSet<T> newHashSet() {
HashSet<T> set = new HashSet<>();
set.add((T)"insert_mock");
return set;
}
@MockMethod
private <E> boolean add(Set s, E e) {
s.add(e.toString() + "_mocked");
return true;
}
/* 第二种写法使用Object类型 */
/* Second solution: use object type */
//@MockMethod
//private static List<Object> getList(DemoTemplate self, Object value) {
// return new ArrayList<Object>() {{ add(value.toString() + "_mock_list"); }};
//}
//
//@MockMethod
//private static Map<Object, Object> getMap(DemoTemplate self, Object key, Object value) {
// return new HashMap<Object, Object>() {{ put(key, value.toString() + "_mock_map"); }};
//}
//
//@MockConstructor
//private HashSet newHashSet() {
// HashSet<Object> set = new HashSet<>();
// set.add("insert_mock");
// return set;
//}
//
//@MockMethod
//private boolean add(Set s, Object e) {
// s.add(e.toString() + "_mocked");
// return true;
//}
@Test
public void should_able_to_mock_single_template_method() {
String res = demoTemplate.singleTemplateMethod();
assertEquals("demo_mock_list", res);
}
@Test
public void should_able_to_mock_double_template_method() {
String res = demoTemplate.doubleTemplateMethod();
assertEquals("testable_mock_map", res);
}
@Test
public void should_able_to_mock_new_template_method() {
Set<?> res = demoTemplate.newTemplateMethod();
assertEquals(2, res.size());
Iterator<?> iterator = res.stream().iterator();
assertEquals("insert_mock", iterator.next());
assertEquals("world_mocked", iterator.next());
}
}

View File

@@ -1,2 +0,0 @@
omni.constructor.enhance.enable = true
mock.target.checking.enable = true

View File

@@ -9,7 +9,6 @@ version = "1.0.0-SNAPSHOT"
java.sourceCompatibility = JavaVersion.VERSION_1_8
repositories {
mavenLocal()
mavenCentral()
}
@@ -17,8 +16,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.7.9")
testAnnotationProcessor("com.alibaba.testable:testable-processor:0.7.9")
testImplementation("com.alibaba.testable:testable-all:0.4.11")
testAnnotationProcessor("com.alibaba.testable:testable-processor:0.4.11")
}
tasks.withType<KotlinCompile> {

View File

@@ -13,8 +13,8 @@
<kotlin.version>1.3.72</kotlin.version>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<junit.version>5.9.1</junit.version>
<testable.version>0.7.9</testable.version>
<junit.version>5.6.2</junit.version>
<testable.version>0.4.11</testable.version>
</properties>
<dependencies>
@@ -50,7 +50,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M7</version>
<version>3.0.0-M5</version>
<!-- <configuration>-->
<!-- <argLine>@{argLine} -javaagent:${settings.localRepository}/com/alibaba/testable/testable-agent/${testable.version}/testable-agent-${testable.version}.jar</argLine>-->
<!-- </configuration>-->
@@ -71,7 +71,7 @@
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.8</version>
<version>0.8.6</version>
<executions>
<execution>
<id>prepare-agent</id>

Some files were not shown because too many files have changed in this diff Show More