Compare commits

...

72 Commits
v0.6.4 ... 0.7

Author SHA1 Message Date
金戟
c2d456da97 use MockInvoke and MockNew 2021-11-03 11:44:44 +08:00
金戟
28500558fc rename verify() to verifyInvoked(), avoid name conflict with system method 2021-11-02 23:39:48 +08:00
金戟
990f407ebe should throw NullPointerException when invoke mock method on null object (issue-163) 2021-11-02 23:09:57 +08:00
金戟
f6206ee294 add release note of 0.6.10 2021-10-31 23:36:32 +08:00
金戟
8cfba333ff release v0.6.10 2021-10-31 14:24:10 +08:00
Fan Lin
397fc3d2fa Merge pull request #234 from zcbbpo/FIXED-LAMBDA-MR
[Lambda] Fixed method reference external var
2021-10-31 14:19:02 +08:00
jim cao
b7a076dfaf fixed external var 2021-10-30 19:40:21 +08:00
金戟
aee2ec2767 fix typo and format code 2021-10-27 07:33:08 +08:00
Fan Lin
682d822249 Merge pull request #231 from HankDevelop/0.6
接口继承支持
2021-10-27 07:27:46 +08:00
Fan Lin
6cf3f993ca Merge branch 'master' into 0.6 2021-10-27 07:27:23 +08:00
HankDevelop
0cce5a6ec0 接口继承支持 2021-10-26 19:57:10 +08:00
金戟
0917406c44 release v0.6.9 2021-10-17 22:56:33 +08:00
金戟
3bf19ee6d0 fix NullPointerException when passing null as parameter of private accessor 2021-10-17 22:56:33 +08:00
Fan Lin
e1afb1cd54 Merge pull request #223 from Augustine-C/fix_flaky
Fixed a flaky test in core.tool.OmniAccessorTest
2021-09-30 10:47:59 +08:00
Augustine Cui
10953ff02a Fixed a flaky test in core.tool.OmniAccessorTest
This test is flaky since the method `java.lang.Class.getDeclaredFields` is non-deterministic, and `GetDeclaredFilelds`  is ultimately used in the `com.alibaba.testable.core.util.TypeUtil.getAllFields`
I converted the result to a `HashSet` to avoid ordering issues, so the specific ordering of the fields would not affect the result. I think this change can prevent test failure due to future change in the JVM.
2021-09-29 17:10:37 -07:00
金戟
80b0f3b878 do not introduce sun package dependence 2021-09-28 21:26:16 +08:00
Fan Lin
8fbd459280 Merge pull request #208 from zcbbpo/FIXED-LAMBDA-METHOD-REFERENCE-ISSUES
Fixed method reference
2021-09-28 20:46:51 +08:00
jimcao
f3309f933e fixed method reference 2021-09-24 15:20:13 +08:00
金戟
b47204e329 hold images in cdn 2021-09-15 21:11:29 +08:00
金戟
6616ff1174 add description about modify static final member 2021-08-09 08:56:40 +08:00
金戟
09ecd0d7c0 release v0.6.8 2021-08-08 21:53:34 +08:00
金戟
93db467f43 update maintain plan 2021-08-08 21:33:58 +08:00
金戟
a78931ae81 allow modify static final field 2021-07-14 21:29:32 +08:00
金戟
93ad1fa94f allow to dump single mock class 2021-07-12 21:41:31 +08:00
金戟
2780ccc6fa for better performance 2021-07-12 20:37:13 +08:00
金戟
fd3b088f59 release v0.6.7 2021-06-11 17:36:43 +08:00
金戟
e812aa6126 fix stack effect value of xaload opcode 2021-06-11 17:25:38 +08:00
金戟
b949f57d54 in favor of static import 2021-06-06 23:04:42 +08:00
金戟
0551240c7d fix incorrect class name in mock target checking log 2021-06-01 19:40:22 +08:00
金戟
8a3a0cf6bf add with thread pool doc 2021-06-01 10:58:00 +08:00
金戟
d3ab246c78 should travel parent classes 2021-05-31 23:36:53 +08:00
金戟
142b0e5a1e update doc for v0.6.6 2021-05-27 19:03:11 +08:00
金戟
38221a67ef release 0.6.6 2021-05-22 19:00:35 +08:00
金戟
993c178adf fit mock target checking for kotlin 2021-05-22 18:31:03 +08:00
金戟
3786c6251e always transform all test classes 2021-05-22 18:12:26 +08:00
金戟
e5f8e5244c construct void object is unnecessary 2021-05-22 16:36:06 +08:00
金戟
e13dce7ac6 prefer to use minimal exception count 2021-05-22 11:12:58 +08:00
金戟
3265803dec expose constructor of void class 2021-05-22 01:27:12 +08:00
金戟
ef3891bd57 add release note 2021-05-22 01:24:40 +08:00
金戟
fcbdb7275b add test case of basic type constructing 2021-05-21 00:25:02 +08:00
金戟
ea4c6c5b3e allow disable mock target checking 2021-05-16 23:16:51 +08:00
金戟
381ff52515 also check methods from parent classes 2021-05-15 16:16:01 +08:00
金戟
c5f39dfae3 refactor logs, print mock method information in mock class handler 2021-05-15 15:57:02 +08:00
金戟
7f8ef70e57 move byte code logic to util class 2021-05-15 15:20:16 +08:00
金戟
23af64475a support target exist checking of mock method 2021-05-15 11:04:45 +08:00
金戟
7181b49211 package prefix should end with a slash 2021-05-14 21:24:34 +08:00
金戟
cd7b9cc922 allow disable mocking 2021-05-11 22:42:31 +08:00
金戟
46288fa0e9 add detail release plan 2021-05-05 23:23:11 +08:00
金戟
e61597ba8b add package mapping doc 2021-05-04 13:22:07 +08:00
金戟
c577301493 support mock package mapping 2021-05-03 14:56:33 +08:00
金戟
a33908db41 support absolute config path and update docs 2021-05-03 11:39:21 +08:00
金戟
4ea97fe13f always print out context path of testable agent 2021-05-03 10:56:36 +08:00
金戟
7cda8ffd36 add plan for mybatis mock function 2021-05-03 10:37:33 +08:00
金戟
1da98a0364 small fix for android demo 2021-05-03 09:30:23 +08:00
金戟
c6d892da2d global exclude robolectric package from omni processor 2021-05-02 20:11:55 +08:00
金戟
d1f2c3da97 add a mini android demo 2021-05-02 20:10:07 +08:00
金戟
f40cfe929a always static import asserts 2021-05-02 09:25:36 +08:00
金戟
96d1f9d633 release v0.6.5 2021-05-02 08:10:58 +08:00
金戟
350802fc9b fix mock method in super class ignored in association scope 2021-05-01 19:28:26 +08:00
金戟
0ae5163435 revert test code 2021-05-01 17:31:13 +08:00
金戟
0e581a7eeb add kotlin demo for mock scope 2021-05-01 17:25:48 +08:00
金戟
b68ff99800 rename construction options 2021-05-01 16:58:02 +08:00
金戟
7754f828cc fix issue of invoking method with single array parameter 2021-05-01 16:05:42 +08:00
金戟
3d62c91291 association check require at least 6 stack space 2021-04-29 13:23:19 +08:00
金戟
14c3d841c5 add demo for test scope 2021-04-28 23:52:52 +08:00
金戟
5f6cf3888c static final field is not assignable during runtime 2021-04-26 08:30:57 +08:00
金戟
75c6a67b8f support construction options 2021-04-25 23:27:51 +08:00
金戟
d11402dd6c update description of pkgPrefix 2021-04-25 18:25:20 +08:00
金戟
db771b9c66 fix an omni constructor issue caused by array-in-array type 2021-04-25 18:24:13 +08:00
金戟
338c7828c8 skip jacoco generated members and do not travel array of basic classes 2021-04-24 23:30:48 +08:00
金戟
8ebe2a5d0c allow constructor with parameter of same type as class under constructing 2021-04-24 18:46:04 +08:00
金戟
4f3e3f0093 throw with information about the class failed to construct 2021-04-24 18:14:53 +08:00
177 changed files with 4192 additions and 1125 deletions

View File

@@ -2,7 +2,7 @@
换种思路写Mock让单元测试更简单。
无需初始化不挑服务框架甭管要换的是私有方法、静态方法、构造方法还是其他任何类的任何方法也甭管要换的对象是怎么创建的。写好Mock定义加个`@MockMethod`注解,一切统统搞定。
无需初始化不挑服务框架甭管要换的是私有方法、静态方法、构造方法还是其他任何类的任何方法也甭管要换的对象是怎么创建的。写好Mock定义加个`@MockInvoke`注解,一切统统搞定。
- 文档https://alibaba.github.io/testable-mock/
- 国内文档镜像http://freyrlin.gitee.io/testable-mock/
@@ -11,17 +11,13 @@
<font size="5">**0.6版本已发布**</font>,从`0.5.x`升级到`0.6.x`版本请参考[版本升级指南](https://alibaba.github.io/testable-mock/#/zh-cn/doc/upgrade-guide)
如果有遇到其他任何使用问题和建议,请直接在[Issues](https://github.com/alibaba/testable-mock/issues)中提出,也可通过[Pull Request](https://github.com/alibaba/testable-mock/pulls)提交您的代码我们将在24小时内回复并处理
-----
## 版本计划
## 项目维护说明
`TestableMock`正在持续迭代演进,以下版本计划可能在开发过程中发生调整
由于当前并行项目较多,此项目暂时转入维护阶段,在此期间`TestableMock`会继续提供不定期的版本更新。
- `0.6` 当前版本,进行中的工作内容参考[Issue](https://github.com/alibaba/testable-mock/issues)清单
- `0.7` 完善Mock功能的防代码重构机制支持快速Mock指定类型的所有方法
- `1.0` 功能稳定,一个崭新的开始
如果有遇到其他任何使用问题和建议,请直接在[Issues](https://github.com/alibaba/testable-mock/issues)中提出,也可通过[Pull Request](https://github.com/alibaba/testable-mock/pulls)提交您的代码,我们将尽快回复并处理。
## 目录结构

View File

@@ -3,7 +3,7 @@
Write mock faster, make unit testing easier.
Any test framework, no initialization, no matter private method, static method, construction method, or any other method of any class, and no matter how the object created.
Write a mock method, add an `@MockMethod` annotation, everything is done.
Write a mock method, add an `@MockInvoke` annotation, everything is done.
Usage Document: https://alibaba.github.io/testable-mock/#/en-us/
@@ -12,7 +12,7 @@ Usage Document: https://alibaba.github.io/testable-mock/#/en-us/
`TestableMock` is still under heavy development, the following version plans may be adjusted during the iteration
- `v0.6` it's the current version, refer to the [issue](https://github.com/alibaba/testable-mock/issues) list for the work in progress
- `v0.7` better anti-refactor support of mocking, allow quick mock all method in specified class
- `v0.7` better anti-refactoring support of mocking, allow quick mock all method in specified class
- `v1.0` all functions are stable, a brand-new start
## Directory Structure

15
demo/android-demo/.gitignore vendored Normal file
View File

@@ -0,0 +1,15 @@
*.iml
.gradle
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
local.properties

1
demo/android-demo/app/.gitignore vendored Normal file
View File

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

View File

@@ -0,0 +1,55 @@
plugins {
id 'com.android.application'
id 'kotlin-android'
}
android {
compileSdkVersion 30
buildToolsVersion "30.0.3"
defaultConfig {
applicationId "com.alibaba.testable.demo"
minSdkVersion 30
targetSdkVersion 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
testOptions {
unitTests {
includeAndroidResources = true
all {
jvmArgs "-javaagent:${classpath.find { it.name.contains("testable-agent") }.absolutePath}"
}
}
}
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.2.0'
implementation 'androidx.appcompat:appcompat:1.3.0-rc01'
implementation 'com.google.android.material:material:1.1.0'
testImplementation 'androidx.test:core:1.4.0-alpha05'
testImplementation 'androidx.test:runner:1.4.0-alpha05'
testImplementation 'junit:junit:4.+'
testImplementation 'org.robolectric:robolectric:4.5.1'
testImplementation 'com.alibaba.testable:testable-all:0.6.10'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}

View File

@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View File

@@ -0,0 +1,24 @@
package com.alibaba.testable.demo
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.alibaba.testable.demo", appContext.packageName)
}
}

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.alibaba.testable.demo">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.TestableAndroidDemo" />
</manifest>

View File

@@ -0,0 +1,63 @@
package com.alibaba.testable.demo;
import com.alibaba.testable.demo.model.BlackBox;
/**
* 演示基本的Mock功能
* Demonstrate basic mock functionality
*/
public class DemoBasic {
/**
* method with new operation
*/
public String newFunc() {
BlackBox component = new BlackBox("something");
return component.get();
}
/**
* method with member method invoke
*/
public String outerFunc(String s) throws Exception {
return "{ \"res\": \"" + innerFunc(s) + staticFunc() + "\"}";
}
/**
* method with common method invoke
*/
public String commonFunc() {
return "anything".trim() + "__" + "anything".substring(1, 2) + "__" + "abc".startsWith("ab");
}
/**
* method with static method invoke
*/
public BlackBox getBox() {
return BlackBox.secretBox();
}
/**
* two methods invoke same private method
*/
public String callerOne() {
return callFromDifferentMethod();
}
public String callerTwo() {
return callFromDifferentMethod();
}
private static String staticFunc() {
return "_STATIC_TAIL";
}
private String innerFunc(String s) {
return "Nothing";
}
private String callFromDifferentMethod() {
return "realOne";
}
}

View File

@@ -0,0 +1,32 @@
package com.alibaba.testable.demo;
import android.app.Service;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.IBinder;
import android.util.Log;
/**
* 演示与Robolectric配合使用
* Demonstrate using mock along with Robolectric
*/
public class DemoService extends Service {
static final String TAG = "DemoService";
SharedPreferences mPreference;
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if(intent != null && "start_foreground".equalsIgnoreCase(intent.getAction())) {
Log.d(TAG, "start service.");
} else if(intent != null && "stop_foreground".equalsIgnoreCase(intent.getAction())) {
Log.d(TAG, "stop service.");
}
return START_REDELIVER_INTENT;
}
}

View File

@@ -0,0 +1,23 @@
package com.alibaba.testable.demo.model;
public class BlackBox extends Box implements Color {
public BlackBox(String data) {
this.data = data;
}
public static BlackBox secretBox() {
return new BlackBox("secret");
}
@Override
public void put(String something) {
data = something;
}
@Override
public String getColor() {
return "black";
}
}

View File

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

View File

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

View File

@@ -0,0 +1,30 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
<aapt:attr name="android:fillColor">
<gradient
android:endX="85.84757"
android:endY="92.4963"
android:startX="42.9492"
android:startY="49.59793"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>

View File

@@ -0,0 +1,170 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
</vector>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -0,0 +1,16 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Theme.TestableAndroidDemo" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
<!-- Primary brand color. -->
<item name="colorPrimary">@color/purple_200</item>
<item name="colorPrimaryVariant">@color/purple_700</item>
<item name="colorOnPrimary">@color/black</item>
<!-- Secondary brand color. -->
<item name="colorSecondary">@color/teal_200</item>
<item name="colorSecondaryVariant">@color/teal_200</item>
<item name="colorOnSecondary">@color/black</item>
<!-- Status bar color. -->
<item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
<!-- Customize your theme here. -->
</style>
</resources>

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="purple_200">#FFBB86FC</color>
<color name="purple_500">#FF6200EE</color>
<color name="purple_700">#FF3700B3</color>
<color name="teal_200">#FF03DAC5</color>
<color name="teal_700">#FF018786</color>
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color>
</resources>

View File

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

View File

@@ -0,0 +1,16 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Theme.TestableAndroidDemo" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
<!-- Primary brand color. -->
<item name="colorPrimary">@color/purple_500</item>
<item name="colorPrimaryVariant">@color/purple_700</item>
<item name="colorOnPrimary">@color/white</item>
<!-- Secondary brand color. -->
<item name="colorSecondary">@color/teal_200</item>
<item name="colorSecondaryVariant">@color/teal_700</item>
<item name="colorOnSecondary">@color/black</item>
<!-- Status bar color. -->
<item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
<!-- Customize your theme here. -->
</style>
</resources>

View File

@@ -0,0 +1,122 @@
package com.alibaba.testable.demo;
import com.alibaba.testable.demo.model.BlackBox;
import com.alibaba.testable.core.annotation.MockNew;
import com.alibaba.testable.core.annotation.MockInvoke;
import org.junit.Test;
import java.util.concurrent.Executors;
import static com.alibaba.testable.core.matcher.InvocationVerifier.verifyInvoked;
import static com.alibaba.testable.core.tool.TestableTool.MOCK_CONTEXT;
import static com.alibaba.testable.core.tool.TestableTool.SOURCE_METHOD;
import static org.junit.Assert.assertEquals;
/**
* 演示基本的Mock功能
* Demonstrate basic mock functionality
*/
public class DemoBasicTest {
private DemoBasic demoBasic = new DemoBasic();
public static class Mock {
@MockNew
private BlackBox createBlackBox(String text) {
return new BlackBox("mock_" + text);
}
@MockInvoke(targetClass = DemoBasic.class)
private String innerFunc(String text) {
return "mock_" + text;
}
@MockInvoke(targetClass = DemoBasic.class)
private String staticFunc() {
return "_MOCK_TAIL";
}
@MockInvoke(targetClass = String.class)
private String trim() {
return "trim_string";
}
@MockInvoke(targetClass = String.class, targetMethod = "substring")
private String sub(int i, int j) {
return "sub_string";
}
@MockInvoke(targetClass = String.class)
private boolean startsWith(String s) {
return false;
}
@MockInvoke(targetClass = BlackBox.class)
private BlackBox secretBox() {
return new BlackBox("not_secret_box");
}
@MockInvoke(targetClass = DemoBasic.class)
private String callFromDifferentMethod() {
if ("special_case".equals(MOCK_CONTEXT.get("case"))) {
return "mock_special";
}
switch (SOURCE_METHOD) {
case "callerOne":
return "mock_one";
default:
return "mock_others";
}
}
}
@Test
public void should_mock_new_object() {
assertEquals("mock_something", demoBasic.newFunc());
verifyInvoked("createBlackBox").with("something");
}
@Test
public void should_mock_member_method() throws Exception {
assertEquals("{ \"res\": \"mock_hello_MOCK_TAIL\"}", demoBasic.outerFunc("hello"));
verifyInvoked("innerFunc").with("hello");
verifyInvoked("staticFunc").with();
}
@Test
public void should_mock_common_method() {
assertEquals("trim_string__sub_string__false", demoBasic.commonFunc());
verifyInvoked("trim").withTimes(1);
verifyInvoked("sub").withTimes(1);
verifyInvoked("startsWith").withTimes(1);
}
@Test
public void should_mock_static_method() {
assertEquals("not_secret_box", demoBasic.getBox().get());
verifyInvoked("secretBox").withTimes(1);
}
@Test
public void should_get_source_method_name() throws Exception {
// synchronous
assertEquals("mock_one_mock_others", demoBasic.callerOne() + "_" + demoBasic.callerTwo());
// asynchronous
assertEquals("mock_one_mock_others",
Executors.newSingleThreadExecutor().submit(() -> demoBasic.callerOne() + "_" + demoBasic.callerTwo()).get());
verifyInvoked("callFromDifferentMethod").withTimes(4);
}
@Test
public void should_set_mock_context() throws Exception {
MOCK_CONTEXT.put("case", "special_case");
// synchronous
assertEquals("mock_special", demoBasic.callerOne());
// asynchronous
assertEquals("mock_special", Executors.newSingleThreadExecutor().submit(() -> demoBasic.callerOne()).get());
verifyInvoked("callFromDifferentMethod").withTimes(2);
}
}

View File

@@ -0,0 +1,47 @@
package com.alibaba.testable.demo;
import android.content.Intent;
import android.util.Log;
import com.alibaba.testable.core.annotation.MockInvoke;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import static com.alibaba.testable.core.matcher.InvocationVerifier.verifyInvoked;
@RunWith(RobolectricTestRunner.class)
@Config(sdk = 30)
public class DemoServiceTest {
private DemoService demoService;
public static class Mock {
@MockInvoke(targetClass = Log.class, targetMethod = "d")
public static int log(String tag, String msg) {
return 0;
}
}
@Before
public void setup() throws Exception {
demoService = Robolectric.setupService(DemoService.class);
}
@Test
public void testOnStartCommand() {
Intent intent = new Intent();
intent.setAction("start_foreground");
demoService.onStartCommand(intent, 0, 1);
verifyInvoked("log").with("DemoService", "start service.");
intent.setAction("stop_foreground");
demoService.onStartCommand(intent, 0, 1);
verifyInvoked("log").with("DemoService", "stop service.");
}
}

View File

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

View File

@@ -0,0 +1,28 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext.kotlin_version = "1.4.32"
repositories {
mavenLocal()
google()
jcenter()
}
dependencies {
classpath "com.android.tools.build:gradle:7.0.0-alpha14"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
mavenLocal()
google()
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}

View File

@@ -0,0 +1,21 @@
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
# AndroidX package structure to make it clearer which packages are bundled with the
# Android operating system, and which are packaged with your app"s APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true
# Automatically convert third-party libraries to use AndroidX
android.enableJetifier=true
# Kotlin code style for this project: "official" or "obsolete":
kotlin.code.style=official

View File

@@ -0,0 +1,6 @@
#Fri Apr 30 14:35:40 CST 2021
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0-bin.zip

172
demo/android-demo/gradlew vendored Executable file
View File

@@ -0,0 +1,172 @@
#!/usr/bin/env sh
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"

84
demo/android-demo/gradlew.bat vendored Normal file
View File

@@ -0,0 +1,84 @@
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View File

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

View File

@@ -13,8 +13,8 @@ repositories {
dependencies {
testImplementation('org.junit.jupiter:junit-jupiter:5.6.2')
testImplementation('com.alibaba.testable:testable-all:0.6.4')
testAnnotationProcessor('com.alibaba.testable:testable-processor:0.6.4')
testImplementation('com.alibaba.testable:testable-all:0.6.10')
testAnnotationProcessor('com.alibaba.testable:testable-processor:0.6.10')
}
tasks.withType(JavaCompile) {

View File

@@ -12,7 +12,7 @@
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<junit.version>5.6.2</junit.version>
<testable.version>0.6.4</testable.version>
<testable.version>0.6.10</testable.version>
</properties>
<dependencies>

View File

@@ -0,0 +1,33 @@
package com.alibaba.demo.association;
/**
* 目标类此类中的一些调用将会被Mock掉
* Target class, some invocations inside this class will be mocked
*/
public class CookerService {
private static String hireSandwichCooker() {
return "Real-Sandwich-Cooker";
}
private static String hireHamburgerCooker() {
return "Real-Hamburger-Cooker";
}
private String cookSandwich() {
return "Real-Sandwich";
}
private String cookHamburger() {
return "Real-Hamburger";
}
public String prepareSandwich() {
return hireSandwichCooker() + " & " + cookSandwich();
}
public String prepareHamburger() {
return hireHamburgerCooker() + " & " + cookHamburger();
}
}

View File

@@ -0,0 +1,18 @@
package com.alibaba.demo.association;
/**
* 被测类,会访问`CookerService`里的方法
* Class to be tested, which will access methods in TargetService class
*/
public class SellerService {
private CookerService cookerService = new CookerService();
public String sellSandwich() {
return cookerService.prepareSandwich();
}
public String sellHamburger() {
return cookerService.prepareHamburger();
}
}

View File

@@ -11,7 +11,7 @@ import com.alibaba.demo.basic.model.mock.Color;
public class DemoInherit {
/**
* call method overridden by sub class via parent class variable
* call method overridden by subclass via parent class variable
*/
public Box putIntoBox() {
Box box = new BlackBox("");
@@ -20,7 +20,7 @@ public class DemoInherit {
}
/**
* call method overridden by sub class via sub class variable
* call method overridden by subclass via subclass variable
*/
public BlackBox putIntoBlackBox() {
BlackBox box = new BlackBox("");
@@ -37,7 +37,7 @@ public class DemoInherit {
}
/**
* call method defined in parent class via sub class variable
* call method defined in parent class via subclass variable
*/
public String getFromBlackBox() {
BlackBox box = new BlackBox("data");
@@ -53,10 +53,18 @@ public class DemoInherit {
}
/**
* call method defined in interface via sub class variable
* call method defined in interface via subclass variable
*/
public String getColorViaBox() {
BlackBox box = new BlackBox("");
return box.getColor();
}
/**
* call method defined in interface via subclass variable
*/
public String getColorIdxViaColor() {
Color color = new BlackBox("");
return color.getColorIndex();
}
}

View File

@@ -46,7 +46,9 @@ public class DemoPrivateAccess {
* private member method with arguments
*/
private String privateFuncWithArgs(List<String> list, String str, int i) {
return list.stream().reduce((a, s) -> a + s).orElse("") + " + " + str + " + " + i;
return list.stream().reduce((a, s) -> a + s).orElse("")
+ " + " + (str == null ? "null" : str)
+ " + " + i;
}
}

View File

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

View File

@@ -20,4 +20,8 @@ public class BlackBox extends Box implements Color {
return "black";
}
@Override
public String getColorIndex() {
return "idx";
}
}

View File

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

View File

@@ -0,0 +1,243 @@
package com.alibaba.demo.lambda;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
/**
* @author jimca
*/
@SuppressWarnings({"WrapperTypeMayBePrimitive", "ResultOfMethodCallIgnored", "MismatchedReadAndWriteOfArray", "unused"})
public class ExternalLambdaDemo {
public void string1() {
String s = "";
consumesFunction2(s::contains);
}
public void string2() {
String s = "";
consumesFunction2(s::charAt);
}
public void string3() {
String s = "";
consumesFunction0(s::notify);
}
public void byte1() {
Byte s = 1;
consumesSupplier(s::floatValue);
}
public void byte2() {
Byte s = 1;
consumesFunction2(s::compareTo);
}
public void byte3() {
Byte s = 1;
consumesFunction0(s::notify);
}
public void char1() {
Character s = 1;
consumesFunction0(s::toString);
}
public void char2() {
Character s = 1;
consumesFunction2(s::compareTo);
}
public void char3() {
Character s = 1;
consumesFunction0(s::notify);
}
public void short1() {
Short s = 1;
consumesFunction0(s::toString);
}
public void short2() {
Short s = 1;
consumesFunction2(s::compareTo);
}
public void short3() {
Short s = 1;
consumesFunction0(s::notify);
}
public void int1() {
Integer s = 1;
consumesFunction0(s::toString);
}
public void int2() {
Integer s = 1;
consumesFunction2(s::compareTo);
}
public void int3() {
Integer s = 1;
consumesFunction0(s::notify);
}
public void long1() {
Long s = 1L;
consumesFunction0(s::toString);
}
public void long2() {
Long s = 1L;
consumesFunction2(s::compareTo);
}
public void long3() {
Long s = 1L;
consumesFunction0(s::notify);
}
public void float1() {
Float s = 1f;
consumesFunction0(s::toString);
}
public void float2() {
Float s = 1f;
consumesFunction2(s::compareTo);
}
public void float3() {
Float s = 1f;
consumesFunction0(s::notify);
}
public void double1() {
Double s = 1d;
consumesFunction0(s::toString);
}
public void double2() {
Double s = 1d;
consumesFunction2(s::compareTo);
}
public void double3() {
Double s = 1d;
consumesFunction0(s::notify);
}
public void bool1() {
Boolean s = true;
consumesFunction0(s::toString);
}
public void bool2() {
Boolean s = true;
consumesFunction2(s::compareTo);
}
public void bool3() {
Boolean s = true;
consumesFunction0(s::notify);
}
public void stringArray1() {
String[] array = new String[]{""};
consumesFunction0(array::toString);
}
public void stringArray2() {
String[] array = new String[]{""};
consumesFunction2(array::equals);
}
public void stringArray3() {
String[] array = new String[]{""};
consumesFunction0(array::notify);
}
public void intArray1() {
int[] array = new int[]{1};
consumesFunction0(array::toString);
}
public void intArray2() {
int[] array = new int[]{1};
consumesFunction2(array::equals);
}
public void intArray3() {
int[] array = new int[]{1};
consumesFunction0(array::notify);
}
public void mul() {
String s = "";
consumesTwoFunction2(s::contains, s::contains);
}
public void externalClass() {
LambdaDemo lambdaDemo = new LambdaDemo();
consumesFunction0(lambdaDemo::methodReference0);
}
public void interClass() {
A a = new A();
consumesFunction2(a::m1);
consumesFunction2(a::m2);
}
public void function3() {
ExternalLambdaDemo externalLambdaDemo = new ExternalLambdaDemo();
consumesFunction3(externalLambdaDemo::f3);
}
public Boolean f3(String s1, Long l) {
return false;
}
private void consumesFunction0(Runnable f) {
f.run();
}
private <T> void consumesFunction1(Consumer<T> f) {
f.accept(null);
}
private <T, R> void consumesFunction2(Function<T, R> f) {
f.apply(null);
}
private <T1, T2, R> void consumesFunction3(BiFunction<T1, T2, R> f) {
f.apply(null, null);
}
private <T> void consumesSupplier(Supplier<T> supplier) {
supplier.get();
}
private <T, R> void consumesTwoFunction2(Function<T, R> f1, Function<T, R> f2) {
f1.apply(null);
f2.apply(null);
}
public static class A {
public String m1(int i) {
return "";
}
public String m2(Integer i) {
return "";
}
}
}

View File

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

View File

@@ -0,0 +1,218 @@
package com.alibaba.demo.lambda;
import java.util.ArrayList;
import java.util.Collection;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Stream;
/**
* @author jim
*/
@SuppressWarnings("unused")
public class LambdaDemo {
public void methodReference() {
consumesRun(this::run);
}
private void consumesRun(Runnable r) {
r.run();
}
private void run() {
blackHole();
}
public String methodReference0() {
return consumes0(this::function0);
}
private String consumes0(Supplier<String> function0) {
return function0.get();
}
private String function0() {
return "Hello";
}
public String methodReference1() {
return consumes1(this::function1);
}
private String consumes1(Function<Integer, String> function) {
return function.apply(1);
}
private String function1(Integer i) {
return String.valueOf(i);
}
public String methodReferenceThrows() {
return consumesThrows(this::function1Throwable);
}
private String consumesThrows(Function1Throwable<Integer, String> function) {
try {
return function.apply(1);
}catch (Throwable e) {
e.printStackTrace();
}
return null;
}
@SuppressWarnings("RedundantThrows")
private String function1Throwable(Integer i) throws Throwable{
return String.valueOf(i);
}
public String methodReference2() {
return consumes2(this::function2);
}
private String consumes2(BiFunction<Integer, Double, String> function) {
return function.apply(1, .2);
}
private String function2(Integer i, Double d) {
return i + String.valueOf(d);
}
public String staticMethodReference1() {
return consumes1(StaticMethod::function1);
}
public String staticMethodReference2() {
return consumes2(StaticMethod::function2);
}
public void lambdaRun() {
consumes(() -> System.out.println("lambdaRun"));
}
private void consumes(Runnable o) {
o.run();
}
public void methodReferenceNew() {
Object o = consumes(Object::new);
blackHole(o);
}
private <T> T consumes(Supplier<T> s) {
return s.get();
}
private void blackHole(Object... ignore) {}
public void array() {
Function<Boolean[], Boolean[]> arrayBooleanFunction = this::arrayBooleanFunction;
Function<boolean[], boolean[]> arrayBooleanFunction1 = this::arrayBoolFunction;
Function<Byte[], Byte[]> byteFunction = this::arrayByteFunction;
Function<byte[], byte[]> byteFunction1 = this::arrayByteFunction;
Function<Character[], Character[]> charFunction = this::arrayCharFunction;
Function<char[], char[]> charFunction1 = this::arrayCharFunction;
Function<Short[], Short[]> shortFunction = this::arrayShortFunction;
Function<short[], short[]> shortFunction1 = this::arrayShortFunction;
Function<int[], int[]> intFunction = this::arrayIntFunction;
Function<Integer[], Integer[]> intFunction1 = this::arrayIntegerFunction;
Function<long[], long[]> longFunction = this::arrayLongFunction;
Function<Long[], Long[]> longFunction1 = this::arrayLongFunction;
Function<Float[], Float[]> floatFunction = this::arrayFloatFunction;
Function<float[], float[]> floatFunction1 = this::arrayFloatFunction;
Function<Double[], Double[]> doubleFunction = this::arrayDoubleFunction;
Function<double[], double[]> doubleFunction1 = this::arrayDoubleFunction;
blackHole(arrayBooleanFunction, arrayBooleanFunction1,
byteFunction, byteFunction1, charFunction, charFunction1, shortFunction, shortFunction1,
intFunction, intFunction1, longFunction, longFunction1, floatFunction, floatFunction1, doubleFunction,
doubleFunction1
);
}
private int[] arrayIntFunction(int[] arg) {
return arg;
}
private Integer[] arrayIntegerFunction(Integer[] arg) {
return arg;
}
private boolean[] arrayBoolFunction(boolean[] arg) {
return arg;
}
private Boolean[] arrayBooleanFunction(Boolean[] arg) {
return arg;
}
private byte[] arrayByteFunction(byte[] arg) {
return arg;
}
private Byte[] arrayByteFunction(Byte[] arg) {
return arg;
}
private char[] arrayCharFunction(char[] arg) {
return arg;
}
private Character[] arrayCharFunction(Character[] arg) {
return arg;
}
private short[] arrayShortFunction(short[] arg) {
return arg;
}
private Short[] arrayShortFunction(Short[] arg) {
return arg;
}
private long[] arrayLongFunction(long[] arg) {
return arg;
}
private Long[] arrayLongFunction(Long[] arg) {
return arg;
}
private float[] arrayFloatFunction(float[] arg) {
return arg;
}
private Float[] arrayFloatFunction(Float[] arg) {
return arg;
}
private double[] arrayDoubleFunction(double[] arg) {
return arg;
}
private Double[] arrayDoubleFunction(Double[] arg) {
return arg;
}
public void generic() {
Function<?, ?> genericFunction = this::genericFunction;
blackHole(genericFunction);
}
public <T, R> R genericFunction(T arg) {
//noinspection unchecked
return (R)arg;
}
private void collects() {
long l = Stream.of("1", "2", "3")
.filter(v -> !"2".equals(v))
.map(Long::parseLong)
.peek(this::blackHole)
.map(v -> new ArrayList<Long>(){{add(v);}})
.flatMap(Collection::stream)
.mapToLong(Long::valueOf)
.sum();
blackHole(l);
}
}

View File

@@ -0,0 +1,15 @@
package com.alibaba.demo.lambda;
/**
* @author jim
*/
public class StaticMethod {
public static String function1(Integer i) {
return "static" + i;
}
public static String function2(Integer i, Double d) {
return "static" + i + d;
}
}

View File

@@ -0,0 +1,27 @@
package com.alibaba.demo.association;
import com.alibaba.testable.core.annotation.MockInvoke;
import com.alibaba.testable.core.model.MockScope;
class CookerServiceMock {
@MockInvoke(targetClass = CookerService.class)
public static String hireSandwichCooker() {
return "Fake-Sandwich-Cooker";
}
@MockInvoke(targetClass = CookerService.class, scope = MockScope.ASSOCIATED)
public static String hireHamburgerCooker() {
return "Fake-Hamburger-Cooker";
}
@MockInvoke(targetClass = CookerService.class)
private String cookSandwich() {
return "Faked-Sandwich";
}
@MockInvoke(targetClass = CookerService.class, scope = MockScope.ASSOCIATED)
private String cookHamburger() {
return "Faked-Hamburger";
}
}

View File

@@ -0,0 +1,21 @@
package com.alibaba.demo.association;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class SellerServiceTest {
private SellerService sellerService = new SellerService();
@Test
void should_sell_sandwich() {
assertEquals("Fake-Sandwich-Cooker & Faked-Sandwich", sellerService.sellSandwich());
}
@Test
void should_sell_hamburger() {
assertEquals("Real-Hamburger-Cooker & Real-Hamburger", sellerService.sellHamburger());
}
}

View File

@@ -3,10 +3,10 @@ package com.alibaba.demo.basic;
import com.alibaba.demo.basic.model.mock.BlackBox;
import com.alibaba.demo.basic.model.mock.Box;
import com.alibaba.demo.basic.model.mock.Color;
import com.alibaba.testable.core.annotation.MockMethod;
import com.alibaba.testable.core.annotation.MockInvoke;
import org.junit.jupiter.api.Test;
import static com.alibaba.testable.core.matcher.InvokeVerifier.verify;
import static com.alibaba.testable.core.matcher.InvocationVerifier.verifyInvoked;
import static org.junit.jupiter.api.Assertions.assertEquals;
/**
@@ -18,77 +18,88 @@ class DemoInheritTest {
private DemoInherit demoInherit = new DemoInherit();
public static class Mock {
@MockMethod(targetMethod = "put")
@MockInvoke(targetMethod = "put")
private void put_into_box(Box self, String something) {
self.put("put_" + something + "_into_box");
}
@MockMethod(targetMethod = "put")
@MockInvoke(targetMethod = "put")
private void put_into_blackbox(BlackBox self, String something) {
self.put("put_" + something + "_into_blackbox");
}
@MockMethod(targetMethod = "get")
@MockInvoke(targetMethod = "get")
private String get_from_box(Box self) {
return "get_from_box";
}
@MockMethod(targetMethod = "get")
@MockInvoke(targetMethod = "get")
private String get_from_blackbox(BlackBox self) {
return "get_from_blackbox";
}
@MockMethod(targetMethod = "getColor")
@MockInvoke(targetMethod = "getColor")
private String get_color_from_color(Color self) {
return "color_from_color";
}
@MockMethod(targetMethod = "getColor")
@MockInvoke(targetMethod = "getColor")
private String get_color_from_blackbox(BlackBox self) {
return "color_from_blackbox";
}
@MockInvoke(targetMethod = "getColorIndex")
private String get_colorIdx_from_color(Color self) {
return "colorIdx_from_color";
}
}
@Test
void should_mock_call_sub_object_method_by_parent_object() {
BlackBox box = (BlackBox)demoInherit.putIntoBox();
verify("put_into_box").withTimes(1);
verifyInvoked("put_into_box").withTimes(1);
assertEquals("put_data_into_box", box.get());
}
@Test
void should_mock_call_sub_object_method_by_sub_object() {
BlackBox box = demoInherit.putIntoBlackBox();
verify("put_into_blackbox").withTimes(1);
verifyInvoked("put_into_blackbox").withTimes(1);
assertEquals("put_data_into_blackbox", box.get());
}
@Test
void should_mock_call_parent_object_method_by_parent_object() {
String content = demoInherit.getFromBox();
verify("get_from_box").withTimes(1);
verifyInvoked("get_from_box").withTimes(1);
assertEquals("get_from_box", content);
}
@Test
void should_mock_call_parent_object_method_by_sub_object() {
String content = demoInherit.getFromBlackBox();
verify("get_from_blackbox").withTimes(1);
verifyInvoked("get_from_blackbox").withTimes(1);
assertEquals("get_from_blackbox", content);
}
@Test
void should_mock_call_interface_method_by_interface_object() {
String color = demoInherit.getColorViaColor();
verify("get_color_from_color").withTimes(1);
verifyInvoked("get_color_from_color").withTimes(1);
assertEquals("color_from_color", color);
}
@Test
void should_mock_call_interface_method_by_sub_class_object() {
String color = demoInherit.getColorViaBox();
verify("get_color_from_blackbox").withTimes(1);
verifyInvoked("get_color_from_blackbox").withTimes(1);
assertEquals("color_from_blackbox", color);
}
@Test
void should_mock_call_interface_method_by_sub_interface_object() {
String colorIdx = demoInherit.getColorIdxViaColor();
verifyInvoked("get_colorIdx_from_color").withTimes(1);
assertEquals("colorIdx_from_color", colorIdx);
}
}

View File

@@ -1,6 +1,6 @@
package com.alibaba.demo.basic;
import com.alibaba.testable.core.annotation.MockMethod;
import com.alibaba.testable.core.annotation.MockInvoke;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -12,7 +12,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
class DemoInnerClassTest {
public static class Mock {
@MockMethod(targetClass = DemoInnerClass.class)
@MockInvoke(targetClass = DemoInnerClass.class)
String methodToBeMock() {
return "MockedCall";
}

View File

@@ -1,12 +1,12 @@
package com.alibaba.demo.basic;
import com.alibaba.demo.basic.model.mock.BlackBox;
import com.alibaba.testable.core.annotation.MockMethod;
import com.alibaba.testable.core.annotation.MockInvoke;
import com.alibaba.testable.core.error.VerifyFailedError;
import org.junit.jupiter.api.Test;
import static com.alibaba.testable.core.matcher.InvokeMatcher.*;
import static com.alibaba.testable.core.matcher.InvokeVerifier.verify;
import static com.alibaba.testable.core.matcher.InvocationMatcher.*;
import static com.alibaba.testable.core.matcher.InvocationVerifier.verifyInvoked;
import static org.junit.jupiter.api.Assertions.fail;
/**
@@ -18,62 +18,62 @@ class DemoMatcherTest {
private DemoMatcher demoMatcher = new DemoMatcher();
public static class Mock {
@MockMethod(targetMethod = "methodToBeMocked")
@MockInvoke(targetMethod = "methodToBeMocked")
private void methodWithoutArgument(DemoMatcher self) {}
@MockMethod(targetMethod = "methodToBeMocked")
@MockInvoke(targetMethod = "methodToBeMocked")
private void methodWithArguments(DemoMatcher self, Object a1, Object a2) {}
@MockMethod(targetMethod = "methodToBeMocked")
@MockInvoke(targetMethod = "methodToBeMocked")
private void methodWithArrayArgument(DemoMatcher self, Object[] a) {}
}
@Test
void should_match_no_argument() {
demoMatcher.callMethodWithoutArgument();
verify("methodWithoutArgument").withTimes(1);
verifyInvoked("methodWithoutArgument").withTimes(1);
demoMatcher.callMethodWithoutArgument();
verify("methodWithoutArgument").withTimes(2);
verifyInvoked("methodWithoutArgument").withTimes(2);
}
@Test
void should_match_number_arguments() {
demoMatcher.callMethodWithNumberArguments();
verify("methodWithArguments").without(anyString(), 2);
verify("methodWithArguments").withInOrder(anyInt(), 2);
verify("methodWithArguments").withInOrder(anyLong(), anyNumber());
verify("methodWithArguments").with(1.0, anyMapOf(Integer.class, Float.class));
verify("methodWithArguments").with(anyList(), anySetOf(Float.class));
verify("methodWithArguments").with(anyList(), anyListOf(Float.class));
verify("methodWithArrayArgument").with(anyArrayOf(Long.class));
verify("methodWithArrayArgument").with(anyArray());
verifyInvoked("methodWithArguments").without(anyString(), 2);
verifyInvoked("methodWithArguments").withInOrder(anyInt(), 2);
verifyInvoked("methodWithArguments").withInOrder(anyLong(), anyNumber());
verifyInvoked("methodWithArguments").with(1.0, anyMapOf(Integer.class, Float.class));
verifyInvoked("methodWithArguments").with(anyList(), anySetOf(Float.class));
verifyInvoked("methodWithArguments").with(anyList(), anyListOf(Float.class));
verifyInvoked("methodWithArrayArgument").with(anyArrayOf(Long.class));
verifyInvoked("methodWithArrayArgument").with(anyArray());
}
@Test
void should_match_string_arguments() {
demoMatcher.callMethodWithStringArgument();
verify("methodWithArguments").with(startsWith("he"), endsWith("ld"));
verify("methodWithArguments").with(contains("stab"), matches("m.[cd]k"));
verify("methodWithArrayArgument").with(anyArrayOf(String.class));
verifyInvoked("methodWithArguments").with(startsWith("he"), endsWith("ld"));
verifyInvoked("methodWithArguments").with(contains("stab"), matches("m.[cd]k"));
verifyInvoked("methodWithArrayArgument").with(anyArrayOf(String.class));
}
@Test
void should_match_object_arguments() {
demoMatcher.callMethodWithObjectArgument();
verify("methodWithArguments").withInOrder(any(BlackBox.class), any(BlackBox.class));
verify("methodWithArguments").withInOrder(nullable(BlackBox.class), nullable(BlackBox.class));
verify("methodWithArguments").withInOrder(isNull(), notNull());
verifyInvoked("methodWithArguments").withInOrder(any(BlackBox.class), any(BlackBox.class));
verifyInvoked("methodWithArguments").withInOrder(nullable(BlackBox.class), nullable(BlackBox.class));
verifyInvoked("methodWithArguments").withInOrder(isNull(), notNull());
}
@Test
void should_match_with_times() {
demoMatcher.callMethodWithNumberArguments();
verify("methodWithArguments").with(anyNumber(), any()).times(3);
verifyInvoked("methodWithArguments").with(anyNumber(), any()).times(3);
demoMatcher.callMethodWithNumberArguments();
boolean gotError = false;
try {
verify("methodWithArguments").with(anyNumber(), any()).times(4);
verifyInvoked("methodWithArguments").with(anyNumber(), any()).times(4);
} catch (VerifyFailedError e) {
gotError = true;
}

View File

@@ -1,14 +1,13 @@
package com.alibaba.demo.basic;
import com.alibaba.demo.basic.model.mock.BlackBox;
import com.alibaba.testable.core.annotation.MockConstructor;
import com.alibaba.testable.core.annotation.MockMethod;
import org.junit.jupiter.api.Assertions;
import com.alibaba.testable.core.annotation.MockNew;
import com.alibaba.testable.core.annotation.MockInvoke;
import org.junit.jupiter.api.Test;
import java.util.concurrent.Executors;
import static com.alibaba.testable.core.matcher.InvokeVerifier.verify;
import static com.alibaba.testable.core.matcher.InvocationVerifier.verifyInvoked;
import static com.alibaba.testable.core.tool.TestableTool.MOCK_CONTEXT;
import static com.alibaba.testable.core.tool.TestableTool.SOURCE_METHOD;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -22,42 +21,42 @@ class DemoMockTest {
private DemoMock demoMock = new DemoMock();
public static class Mock {
@MockConstructor
@MockNew
private BlackBox createBlackBox(String text) {
return new BlackBox("mock_" + text);
}
@MockMethod(targetClass = DemoMock.class)
@MockInvoke(targetClass = DemoMock.class)
private String innerFunc(String text) {
return "mock_" + text;
}
@MockMethod(targetClass = DemoMock.class)
@MockInvoke(targetClass = DemoMock.class)
private String staticFunc() {
return "_MOCK_TAIL";
}
@MockMethod(targetClass = String.class)
@MockInvoke(targetClass = String.class)
private String trim() {
return "trim_string";
}
@MockMethod(targetClass = String.class, targetMethod = "substring")
@MockInvoke(targetClass = String.class, targetMethod = "substring")
private String sub(int i, int j) {
return "sub_string";
}
@MockMethod(targetClass = String.class)
@MockInvoke(targetClass = String.class)
private boolean startsWith(String s) {
return false;
}
@MockMethod(targetClass = BlackBox.class)
@MockInvoke(targetClass = BlackBox.class)
private BlackBox secretBox() {
return new BlackBox("not_secret_box");
}
@MockMethod(targetClass = DemoMock.class)
@MockInvoke(targetClass = DemoMock.class)
private String callFromDifferentMethod() {
if ("special_case".equals(MOCK_CONTEXT.get("case"))) {
return "mock_special";
@@ -74,28 +73,28 @@ class DemoMockTest {
@Test
void should_mock_new_object() {
assertEquals("mock_something", demoMock.newFunc());
verify("createBlackBox").with("something");
verifyInvoked("createBlackBox").with("something");
}
@Test
void should_mock_member_method() throws Exception {
assertEquals("{ \"res\": \"mock_hello_MOCK_TAIL\"}", demoMock.outerFunc("hello"));
verify("innerFunc").with("hello");
verify("staticFunc").with();
verifyInvoked("innerFunc").with("hello");
verifyInvoked("staticFunc").with();
}
@Test
void should_mock_common_method() {
assertEquals("trim_string__sub_string__false", demoMock.commonFunc());
verify("trim").withTimes(1);
verify("sub").withTimes(1);
verify("startsWith").withTimes(1);
verifyInvoked("trim").withTimes(1);
verifyInvoked("sub").withTimes(1);
verifyInvoked("startsWith").withTimes(1);
}
@Test
void should_mock_static_method() {
Assertions.assertEquals("not_secret_box", demoMock.getBox().get());
verify("secretBox").withTimes(1);
assertEquals("not_secret_box", demoMock.getBox().get());
verifyInvoked("secretBox").withTimes(1);
}
@Test
@@ -105,7 +104,7 @@ class DemoMockTest {
// asynchronous
assertEquals("mock_one_mock_others",
Executors.newSingleThreadExecutor().submit(() -> demoMock.callerOne() + "_" + demoMock.callerTwo()).get());
verify("callFromDifferentMethod").withTimes(4);
verifyInvoked("callFromDifferentMethod").withTimes(4);
}
@Test
@@ -115,7 +114,7 @@ class DemoMockTest {
assertEquals("mock_special", demoMock.callerOne());
// asynchronous
assertEquals("mock_special", Executors.newSingleThreadExecutor().submit(() -> demoMock.callerOne()).get());
verify("callFromDifferentMethod").withTimes(2);
verifyInvoked("callFromDifferentMethod").withTimes(2);
}
}

View File

@@ -1,11 +1,11 @@
package com.alibaba.demo.basic;
import com.alibaba.testable.core.tool.PrivateAccessor;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.List;
import static com.alibaba.testable.core.tool.PrivateAccessor.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
@@ -20,39 +20,45 @@ class DemoPrivateAccessorTest {
@Test
void should_access_private_method() {
List<String> list = new ArrayList<String>() {{ add("a"); add("b"); add("c"); }};
assertEquals("member", PrivateAccessor.invoke(demoPrivateAccess, "privateFunc"));
assertEquals("abc + hello + 1", PrivateAccessor.invoke(demoPrivateAccess, "privateFuncWithArgs", list, "hello", 1));
assertEquals("member", invoke(demoPrivateAccess, "privateFunc"));
assertEquals("abc + hello + 1", invoke(demoPrivateAccess, "privateFuncWithArgs", list, "hello", 1));
}
@Test
void should_access_private_field() {
PrivateAccessor.set(demoPrivateAccess, "count", 3);
assertEquals(Integer.valueOf(3), PrivateAccessor.get(demoPrivateAccess, "count"));
set(demoPrivateAccess, "count", 3);
assertEquals(Integer.valueOf(3), get(demoPrivateAccess, "count"));
}
@Test
void should_access_private_static_method() {
assertEquals("static", PrivateAccessor.invokeStatic(DemoPrivateAccess.class, "privateStaticFunc"));
assertEquals("hello + 1", PrivateAccessor.invokeStatic(DemoPrivateAccess.class, "privateStaticFuncWithArgs", "hello", 1));
assertEquals("static", invokeStatic(DemoPrivateAccess.class, "privateStaticFunc"));
assertEquals("hello + 1", invokeStatic(DemoPrivateAccess.class, "privateStaticFuncWithArgs", "hello", 1));
}
@Test
void should_access_private_static_field() {
PrivateAccessor.setStatic(DemoPrivateAccess.class, "staticCount", 3);
assertEquals(Integer.valueOf(3), PrivateAccessor.getStatic(DemoPrivateAccess.class, "staticCount"));
setStatic(DemoPrivateAccess.class, "staticCount", 3);
assertEquals(Integer.valueOf(3), getStatic(DemoPrivateAccess.class, "staticCount"));
}
@Test
void should_update_final_field() {
PrivateAccessor.set(demoPrivateAccess, "pi", 3.14);
assertEquals(Double.valueOf(3.14), PrivateAccessor.get(demoPrivateAccess, "pi"));
set(demoPrivateAccess, "pi", 3.14);
assertEquals(Double.valueOf(3.14), get(demoPrivateAccess, "pi"));
}
@Test
void should_use_null_parameter() {
PrivateAccessor.set(demoPrivateAccess, "pi", null);
assertNull(PrivateAccessor.get(demoPrivateAccess, "pi"));
assertEquals("null + 1", PrivateAccessor.invokeStatic(DemoPrivateAccess.class, "privateStaticFuncWithArgs", null, 1));
set(demoPrivateAccess, "pi", null);
assertNull(get(demoPrivateAccess, "pi"));
List<String> list = new ArrayList<String>() {{ add("a"); add("b"); add("c"); }};
String value = invoke(demoPrivateAccess, "privateFuncWithArgs", list, null, 0);
assertEquals("abc + null + 0", value);
value = invokeStatic(DemoPrivateAccess.class, "privateStaticFuncWithArgs", null, 1);
assertEquals("null + 1", value);
}
}

View File

@@ -1,8 +1,7 @@
package com.alibaba.demo.basic;
import com.alibaba.testable.core.annotation.MockConstructor;
import com.alibaba.testable.core.annotation.MockMethod;
import com.alibaba.demo.basic.DemoTemplate;
import com.alibaba.testable.core.annotation.MockNew;
import com.alibaba.testable.core.annotation.MockInvoke;
import org.junit.jupiter.api.Test;
import java.util.*;
@@ -21,24 +20,24 @@ class DemoTemplateTest {
/* 第一种写法:使用泛型定义 */
/* First solution: use generics type */
@MockMethod
@MockInvoke
private <T> List<T> getList(DemoTemplate self, T value) {
return new ArrayList<T>() {{ add((T)(value.toString() + "_mock_list")); }};
}
@MockMethod
@MockInvoke
private <K, V> Map<K, V> getMap(DemoTemplate self, K key, V value) {
return new HashMap<K, V>() {{ put(key, (V)(value.toString() + "_mock_map")); }};
}
@MockConstructor
@MockNew
private <T> HashSet<T> newHashSet() {
HashSet<T> set = new HashSet<>();
set.add((T)"insert_mock");
return set;
}
@MockMethod
@MockInvoke
private <E> boolean add(Set s, E e) {
s.add(e.toString() + "_mocked");
return true;
@@ -47,24 +46,24 @@ class DemoTemplateTest {
/* 第二种写法使用Object类型 */
/* Second solution: use object type */
//@MockMethod
//@MockInvoke
//private List<Object> getList(DemoTemplate self, Object value) {
// return new ArrayList<Object>() {{ add(value.toString() + "_mock_list"); }};
//}
//
//@MockMethod
//@MockInvoke
//private Map<Object, Object> getMap(DemoTemplate self, Object key, Object value) {
// return new HashMap<Object, Object>() {{ put(key, value.toString() + "_mock_map"); }};
//}
//
//@MockConstructor
//@MockNew
//private HashSet newHashSet() {
// HashSet<Object> set = new HashSet<>();
// set.add("insert_mock");
// return set;
//}
//
//@MockMethod
//@MockInvoke
//private boolean add(Set s, Object e) {
// s.add(e.toString() + "_mocked");
// return true;

View File

@@ -0,0 +1,80 @@
package com.alibaba.demo.lambda;
import com.alibaba.testable.core.annotation.MockInvoke;
import org.junit.jupiter.api.Test;
import static com.alibaba.testable.core.matcher.InvocationVerifier.verifyInvoked;
/**
* @author zcbbpo
*/
public class ExternalLambdaDemoTest {
private final ExternalLambdaDemo lambdaDemo = new ExternalLambdaDemo();
@SuppressWarnings("unused")
public static class Mock {
@MockInvoke(targetClass = String.class, targetMethod = "contains")
public boolean mockContains(CharSequence s) {
return false;
}
@MockInvoke(targetClass = Byte.class, targetMethod = "floatValue")
public float mockFloatValue() {
return 0.1f;
}
@MockInvoke(targetClass = Double.class, targetMethod = "compareTo")
public int mockCompareTo(Double anotherDouble) {
return 1;
}
@MockInvoke(targetClass = LambdaDemo.class, targetMethod = "methodReference0")
public String mockMethodReference0() {
return "";
}
@MockInvoke(targetClass = ExternalLambdaDemo.class, targetMethod = "f3")
public Boolean mockF3(String s1, Long l) {
return true;
}
}
@Test
public void shouldMockString1() {
lambdaDemo.string1();
verifyInvoked("mockContains").withTimes(1);
}
@Test
public void shouldMockByte1() {
lambdaDemo.byte1();
verifyInvoked("mockFloatValue").withTimes(1);
}
@Test
public void shouldMockDouble2() {
lambdaDemo.double2();
verifyInvoked("mockCompareTo").withTimes(1);
}
@Test
public void testMul() {
lambdaDemo.mul();
verifyInvoked("mockContains").withTimes(2);
}
@Test
public void testExternalClass() {
lambdaDemo.externalClass();
verifyInvoked("mockMethodReference0").withTimes(1);
}
@Test
public void testFunction3() {
lambdaDemo.function3();
verifyInvoked("mockF3").withTimes(1);
}
}

View File

@@ -0,0 +1,86 @@
package com.alibaba.demo.lambda;
import com.alibaba.testable.core.annotation.MockInvoke;
import org.junit.jupiter.api.Test;
import static com.alibaba.testable.core.matcher.InvocationVerifier.verifyInvoked;
import static org.junit.jupiter.api.Assertions.assertEquals;
/**
* @author zcbbpo
*/
public class LambdaDemoTest {
private final LambdaDemo lambdaDemo = new LambdaDemo();
@SuppressWarnings("unused")
public static class Mock {
@MockInvoke(targetClass = LambdaDemo.class, targetMethod = "run")
private void mockRun() {
}
@MockInvoke(targetClass = LambdaDemo.class)
private String function0() {
return "mock_function0";
}
@MockInvoke(targetClass = LambdaDemo.class)
private String function1(Integer i) {
return "mock_function1";
}
@MockInvoke(targetClass = LambdaDemo.class)
private String function2(Integer i, Double d) {
return "mock_function2";
}
@SuppressWarnings("RedundantThrows")
@MockInvoke(targetClass = LambdaDemo.class)
private String function1Throwable(Integer i) throws Throwable{
return "mock_function1Throwable";
}
@MockInvoke(targetClass = StaticMethod.class, targetMethod = "function1")
public static String staticFunction1(Integer i) {
return "mock_staticFunction1";
}
}
@Test
public void shouldMockRun() {
lambdaDemo.methodReference();
verifyInvoked("mockRun").withTimes(1);
}
@Test
public void shouldMockFunction0() {
String s = lambdaDemo.methodReference0();
assertEquals(s, "mock_function0");
}
@Test
public void shouldMockFunction1() {
String s = lambdaDemo.methodReference1();
assertEquals(s, "mock_function1");
}
@Test
public void shouldMockFunction2() {
String s = lambdaDemo.methodReference2();
assertEquals(s, "mock_function2");
}
@Test
public void shouldMockFunction1Throws() {
String s = lambdaDemo.methodReferenceThrows();
assertEquals(s, "mock_function1Throwable");
}
@Test
public void shouldMockStaticFunction1() {
String s = lambdaDemo.staticMethodReference1();
assertEquals(s, "mock_staticFunction1");
}
}

View File

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

View File

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

View File

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

View File

@@ -4,7 +4,7 @@ import com.alibaba.testable.core.annotation.MockWith;
import org.junit.jupiter.api.Test;
import static com.alibaba.testable.core.matcher.InvokeVerifier.verify;
import static com.alibaba.testable.core.matcher.InvocationVerifier.verifyInvoked;
import static org.junit.jupiter.api.Assertions.assertEquals;
@MockWith
@@ -19,9 +19,9 @@ public class OneToMultiSvcTest {
assertEquals("a_mock", aSvc.demo("test"));
assertEquals("b_mock", bSvc.demo("test"));
assertEquals("c_mock", cSvc.demo("test"));
verify("a_format").withTimes(1);
verify("b_format").withTimes(1);
verify("c_format").withTimes(1);
verifyInvoked("a_format").withTimes(1);
verifyInvoked("b_format").withTimes(1);
verifyInvoked("c_format").withTimes(1);
}
}

View File

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

View File

@@ -9,6 +9,7 @@ version = "1.0.0-SNAPSHOT"
java.sourceCompatibility = JavaVersion.VERSION_1_8
repositories {
mavenLocal()
mavenCentral()
}
@@ -16,8 +17,8 @@ dependencies {
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
testImplementation("org.junit.jupiter:junit-jupiter:5.6.2")
testImplementation("com.alibaba.testable:testable-all:0.6.4")
testAnnotationProcessor("com.alibaba.testable:testable-processor:0.6.4")
testImplementation("com.alibaba.testable:testable-all:0.6.10")
testAnnotationProcessor("com.alibaba.testable:testable-processor:0.6.10")
}
tasks.withType<KotlinCompile> {

View File

@@ -14,7 +14,7 @@
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<junit.version>5.6.2</junit.version>
<testable.version>0.6.4</testable.version>
<testable.version>0.6.10</testable.version>
</properties>
<dependencies>

View File

@@ -0,0 +1,34 @@
package com.alibaba.demo.association
/**
* 目标类此类中的一些调用将会被Mock掉
* Target class, some invocations inside this class will be mocked
*/
class CookerService {
private fun cookSandwich(): String {
return "Real-Sandwich"
}
private fun cookHamburger(): String {
return "Real-Hamburger"
}
fun prepareSandwich(): String {
return hireSandwichCooker() + " & " + cookSandwich()
}
fun prepareHamburger(): String {
return hireHamburgerCooker() + " & " + cookHamburger()
}
companion object {
private fun hireSandwichCooker(): String {
return "Real-Sandwich-Cooker"
}
private fun hireHamburgerCooker(): String {
return "Real-Hamburger-Cooker"
}
}
}

View File

@@ -0,0 +1,18 @@
package com.alibaba.demo.association
/**
* 被测类,会访问`CookerService`里的方法
* Class to be tested, which will access methods in TargetService class
*/
class SellerService {
private val cookerService = CookerService()
fun sellSandwich(): String {
return cookerService.prepareSandwich()
}
fun sellHamburger(): String {
return cookerService.prepareHamburger()
}
}

View File

@@ -11,7 +11,7 @@ import com.alibaba.demo.basic.model.mock.Color
class DemoInherit {
/**
* call method overridden by sub class via parent class variable
* call method overridden by subclass via parent class variable
*/
fun putIntoBox(): Box {
val box: Box = BlackBox("")
@@ -20,7 +20,7 @@ class DemoInherit {
}
/**
* call method overridden by sub class via sub class variable
* call method overridden by subclass via subclass variable
*/
fun putIntoBlackBox(): BlackBox {
val box = BlackBox("")
@@ -38,7 +38,7 @@ class DemoInherit {
}
/**
* call method defined in parent class via sub class variable
* call method defined in parent class via subclass variable
*/
val fromBlackBox: String?
get() {
@@ -56,7 +56,7 @@ class DemoInherit {
}
/**
* call method defined in interface via sub class variable
* call method defined in interface via subclass variable
*/
val colorViaBox: String
get() {

View File

@@ -0,0 +1,27 @@
package com.alibaba.demo.association
import com.alibaba.testable.core.annotation.MockInvoke
import com.alibaba.testable.core.model.MockScope
internal class CookerServiceMock {
@MockInvoke(targetClass = CookerService::class)
private fun cookSandwich(): String {
return "Faked-Sandwich"
}
@MockInvoke(targetClass = CookerService::class, scope = MockScope.ASSOCIATED)
private fun cookHamburger(): String {
return "Faked-Hamburger"
}
@MockInvoke(targetClass = CookerService::class)
fun hireSandwichCooker(): String {
return "Fake-Sandwich-Cooker"
}
@MockInvoke(targetClass = CookerService::class, scope = MockScope.ASSOCIATED)
fun hireHamburgerCooker(): String {
return "Fake-Hamburger-Cooker"
}
}

View File

@@ -0,0 +1,19 @@
package com.alibaba.demo.association
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
internal class SellerServiceTest {
private val sellerService = SellerService()
@Test
fun should_sell_sandwich() {
assertEquals("Fake-Sandwich-Cooker & Faked-Sandwich", sellerService.sellSandwich())
}
@Test
fun should_sell_hamburger() {
assertEquals("Real-Hamburger-Cooker & Real-Hamburger", sellerService.sellHamburger())
}
}

View File

@@ -1,11 +1,11 @@
package com.alibaba.demo.basic
import com.alibaba.testable.core.annotation.MockMethod
import com.alibaba.testable.core.matcher.InvokeVerifier
import com.alibaba.testable.core.annotation.MockInvoke
import com.alibaba.testable.core.matcher.InvocationVerifier
import com.alibaba.demo.basic.model.mock.BlackBox
import com.alibaba.demo.basic.model.mock.Box
import com.alibaba.demo.basic.model.mock.Color
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
/**
@@ -14,35 +14,35 @@ import org.junit.jupiter.api.Test
*/
internal class DemoInheritTest {
private val demoInherit = com.alibaba.demo.basic.DemoInherit()
private val demoInherit = DemoInherit()
class Mock {
@MockMethod(targetMethod = "put")
@MockInvoke(targetMethod = "put")
private fun put_into_box(self: Box, something: String) {
self.put("put_" + something + "_into_box")
}
@MockMethod(targetMethod = "put")
@MockInvoke(targetMethod = "put")
private fun put_into_blackbox(self: BlackBox, something: String) {
self.put("put_" + something + "_into_blackbox")
}
@MockMethod(targetMethod = "get")
@MockInvoke(targetMethod = "get")
private fun get_from_box(self: Box): String {
return "get_from_box"
}
@MockMethod(targetMethod = "get")
@MockInvoke(targetMethod = "get")
private fun get_from_blackbox(self: BlackBox): String {
return "get_from_blackbox"
}
@MockMethod(targetMethod = "getColor")
@MockInvoke(targetMethod = "getColor")
private fun get_color_from_color(self: Color): String {
return "color_from_color"
}
@MockMethod(targetMethod = "getColor")
@MockInvoke(targetMethod = "getColor")
private fun get_color_from_blackbox(self: BlackBox): String {
return "color_from_blackbox"
}
@@ -51,42 +51,42 @@ internal class DemoInheritTest {
@Test
fun should_mock_call_sub_object_method_by_parent_object() {
val box = demoInherit.putIntoBox() as BlackBox
InvokeVerifier.verify("put_into_box").withTimes(1)
Assertions.assertEquals("put_data_into_box", box.get())
InvocationVerifier.verifyInvoked("put_into_box").withTimes(1)
assertEquals("put_data_into_box", box.get())
}
@Test
fun should_mock_call_sub_object_method_by_sub_object() {
val box = demoInherit.putIntoBlackBox()
InvokeVerifier.verify("put_into_blackbox").withTimes(1)
Assertions.assertEquals("put_data_into_blackbox", box.get())
InvocationVerifier.verifyInvoked("put_into_blackbox").withTimes(1)
assertEquals("put_data_into_blackbox", box.get())
}
@Test
fun should_mock_call_parent_object_method_by_parent_object() {
val content = demoInherit.fromBox
InvokeVerifier.verify("get_from_box").withTimes(1)
Assertions.assertEquals("get_from_box", content)
InvocationVerifier.verifyInvoked("get_from_box").withTimes(1)
assertEquals("get_from_box", content)
}
@Test
fun should_mock_call_parent_object_method_by_sub_object() {
val content = demoInherit.fromBlackBox
InvokeVerifier.verify("get_from_blackbox").withTimes(1)
Assertions.assertEquals("get_from_blackbox", content)
InvocationVerifier.verifyInvoked("get_from_blackbox").withTimes(1)
assertEquals("get_from_blackbox", content)
}
@Test
fun should_mock_call_interface_method_by_interface_object() {
val color = demoInherit.colorViaColor
InvokeVerifier.verify("get_color_from_color").withTimes(1)
Assertions.assertEquals("color_from_color", color)
InvocationVerifier.verifyInvoked("get_color_from_color").withTimes(1)
assertEquals("color_from_color", color)
}
@Test
fun should_mock_call_interface_method_by_sub_class_object() {
val color = demoInherit.colorViaBox
InvokeVerifier.verify("get_color_from_blackbox").withTimes(1)
Assertions.assertEquals("color_from_blackbox", color)
InvocationVerifier.verifyInvoked("get_color_from_blackbox").withTimes(1)
assertEquals("color_from_blackbox", color)
}
}

View File

@@ -1,7 +1,7 @@
package com.alibaba.demo.basic
import com.alibaba.testable.core.annotation.MockMethod
import org.junit.jupiter.api.Assertions
import com.alibaba.testable.core.annotation.MockInvoke
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
/**
@@ -11,7 +11,7 @@ import org.junit.jupiter.api.Test
internal class DemoInnerClassTest {
class Mock {
@MockMethod(targetClass = DemoInnerClass::class)
@MockInvoke(targetClass = DemoInnerClass::class)
fun methodToBeMock(): String {
return "MockedCall"
}
@@ -21,8 +21,8 @@ internal class DemoInnerClassTest {
@Throws(Exception::class)
fun should_mock_invoke_inside_inner_class() {
val demo = DemoInnerClass()
Assertions.assertEquals("MockedCall", demo.callInnerDemo())
Assertions.assertEquals("MockedCall", demo.callAnonymousInner())
Assertions.assertEquals("MockedCall", DemoInnerClass.StaticInner().demo())
assertEquals("MockedCall", demo.callInnerDemo())
assertEquals("MockedCall", demo.callAnonymousInner())
assertEquals("MockedCall", DemoInnerClass.StaticInner().demo())
}
}

View File

@@ -1,9 +1,9 @@
package com.alibaba.demo.basic
import com.alibaba.testable.core.annotation.MockMethod
import com.alibaba.testable.core.annotation.MockInvoke
import com.alibaba.testable.core.error.VerifyFailedError
import com.alibaba.testable.core.matcher.InvokeMatcher
import com.alibaba.testable.core.matcher.InvokeVerifier
import com.alibaba.testable.core.matcher.InvocationMatcher
import com.alibaba.testable.core.matcher.InvocationVerifier
import com.alibaba.demo.basic.model.mock.BlackBox
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Test
@@ -14,69 +14,69 @@ import org.junit.jupiter.api.Test
*/
internal class DemoMatcherTest {
private val demoMatcher = com.alibaba.demo.basic.DemoMatcher()
private val demoMatcher = DemoMatcher()
class Mock {
@MockMethod(targetMethod = "methodToBeMocked")
private fun methodWithoutArgument(self: com.alibaba.demo.basic.DemoMatcher) {
@MockInvoke(targetMethod = "methodToBeMocked")
private fun methodWithoutArgument(self: DemoMatcher) {
}
@MockMethod(targetMethod = "methodToBeMocked")
private fun methodWithArguments(self: com.alibaba.demo.basic.DemoMatcher, a1: Any, a2: Any) {
@MockInvoke(targetMethod = "methodToBeMocked")
private fun methodWithArguments(self: DemoMatcher, a1: Any, a2: Any) {
}
@MockMethod(targetMethod = "methodToBeMocked")
private fun methodWithArrayArgument(self: com.alibaba.demo.basic.DemoMatcher, a: Array<Any>) {
@MockInvoke(targetMethod = "methodToBeMocked")
private fun methodWithArrayArgument(self: DemoMatcher, a: Array<Any>) {
}
}
@Test
fun should_match_no_argument() {
demoMatcher.callMethodWithoutArgument()
InvokeVerifier.verify("methodWithoutArgument").withTimes(1)
InvocationVerifier.verifyInvoked("methodWithoutArgument").withTimes(1)
demoMatcher.callMethodWithoutArgument()
InvokeVerifier.verify("methodWithoutArgument").withTimes(2)
InvocationVerifier.verifyInvoked("methodWithoutArgument").withTimes(2)
}
@Test
fun should_match_number_arguments() {
demoMatcher.callMethodWithNumberArguments()
InvokeVerifier.verify("methodWithArguments").without(InvokeMatcher.anyString(), 2)
InvokeVerifier.verify("methodWithArguments").withInOrder(InvokeMatcher.anyInt(), 2)
InvokeVerifier.verify("methodWithArguments").withInOrder(InvokeMatcher.anyLong(), InvokeMatcher.anyNumber())
InvocationVerifier.verifyInvoked("methodWithArguments").without(InvocationMatcher.anyString(), 2)
InvocationVerifier.verifyInvoked("methodWithArguments").withInOrder(InvocationMatcher.anyInt(), 2)
InvocationVerifier.verifyInvoked("methodWithArguments").withInOrder(InvocationMatcher.anyLong(), InvocationMatcher.anyNumber())
// Note: Must use `::class.javaObjectType` for primary types check in Kotlin
InvokeVerifier.verify("methodWithArguments").with(1.0, InvokeMatcher.anyMapOf(Int::class.javaObjectType, Float::class.javaObjectType)).times(2)
InvokeVerifier.verify("methodWithArguments").with(InvokeMatcher.anyList(), InvokeMatcher.anySetOf(Float::class.javaObjectType)).times(2)
InvokeVerifier.verify("methodWithArguments").with(InvokeMatcher.anyList(), InvokeMatcher.anyListOf(Float::class.javaObjectType))
InvokeVerifier.verify("methodWithArrayArgument").with(InvokeMatcher.anyArrayOf(Long::class.javaObjectType))
InvokeVerifier.verify("methodWithArrayArgument").with(InvokeMatcher.anyArray())
InvocationVerifier.verifyInvoked("methodWithArguments").with(1.0, InvocationMatcher.anyMapOf(Int::class.javaObjectType, Float::class.javaObjectType)).times(2)
InvocationVerifier.verifyInvoked("methodWithArguments").with(InvocationMatcher.anyList(), InvocationMatcher.anySetOf(Float::class.javaObjectType)).times(2)
InvocationVerifier.verifyInvoked("methodWithArguments").with(InvocationMatcher.anyList(), InvocationMatcher.anyListOf(Float::class.javaObjectType))
InvocationVerifier.verifyInvoked("methodWithArrayArgument").with(InvocationMatcher.anyArrayOf(Long::class.javaObjectType))
InvocationVerifier.verifyInvoked("methodWithArrayArgument").with(InvocationMatcher.anyArray())
}
@Test
fun should_match_string_arguments() {
demoMatcher.callMethodWithStringArgument()
InvokeVerifier.verify("methodWithArguments").with(InvokeMatcher.startsWith("he"), InvokeMatcher.endsWith("ld"))
InvokeVerifier.verify("methodWithArguments").with(InvokeMatcher.contains("stab"), InvokeMatcher.matches("m.[cd]k"))
InvokeVerifier.verify("methodWithArrayArgument").with(InvokeMatcher.anyArrayOf(String::class.java))
InvocationVerifier.verifyInvoked("methodWithArguments").with(InvocationMatcher.startsWith("he"), InvocationMatcher.endsWith("ld"))
InvocationVerifier.verifyInvoked("methodWithArguments").with(InvocationMatcher.contains("stab"), InvocationMatcher.matches("m.[cd]k"))
InvocationVerifier.verifyInvoked("methodWithArrayArgument").with(InvocationMatcher.anyArrayOf(String::class.java))
}
@Test
fun should_match_object_arguments() {
demoMatcher.callMethodWithObjectArgument()
InvokeVerifier.verify("methodWithArguments").withInOrder(InvokeMatcher.any(BlackBox::class.java), InvokeMatcher.any(BlackBox::class.java))
InvokeVerifier.verify("methodWithArguments").withInOrder(InvokeMatcher.nullable(BlackBox::class.java), InvokeMatcher.nullable(BlackBox::class.java))
InvokeVerifier.verify("methodWithArguments").withInOrder(InvokeMatcher.isNull(), InvokeMatcher.notNull())
InvocationVerifier.verifyInvoked("methodWithArguments").withInOrder(InvocationMatcher.any(BlackBox::class.java), InvocationMatcher.any(BlackBox::class.java))
InvocationVerifier.verifyInvoked("methodWithArguments").withInOrder(InvocationMatcher.nullable(BlackBox::class.java), InvocationMatcher.nullable(BlackBox::class.java))
InvocationVerifier.verifyInvoked("methodWithArguments").withInOrder(InvocationMatcher.isNull(), InvocationMatcher.notNull())
}
@Test
fun should_match_with_times() {
demoMatcher.callMethodWithNumberArguments()
InvokeVerifier.verify("methodWithArguments").with(InvokeMatcher.anyNumber(), InvokeMatcher.any()).times(4)
InvocationVerifier.verifyInvoked("methodWithArguments").with(InvocationMatcher.anyNumber(), InvocationMatcher.any()).times(4)
demoMatcher.callMethodWithNumberArguments()
var gotError = false
try {
InvokeVerifier.verify("methodWithArguments").with(InvokeMatcher.anyNumber(), InvokeMatcher.any()).times(5)
InvocationVerifier.verifyInvoked("methodWithArguments").with(InvocationMatcher.anyNumber(), InvocationMatcher.any()).times(5)
} catch (e: VerifyFailedError) {
gotError = true
}

View File

@@ -1,8 +1,8 @@
package com.alibaba.demo.basic
import com.alibaba.testable.core.annotation.MockConstructor
import com.alibaba.testable.core.annotation.MockMethod
import com.alibaba.testable.core.matcher.InvokeVerifier.verify
import com.alibaba.testable.core.annotation.MockNew
import com.alibaba.testable.core.annotation.MockInvoke
import com.alibaba.testable.core.matcher.InvocationVerifier.verifyInvoked
import com.alibaba.testable.core.tool.TestableTool.SOURCE_METHOD
import com.alibaba.testable.core.tool.TestableTool.MOCK_CONTEXT
import com.alibaba.demo.basic.model.mock.BlackBox
@@ -20,37 +20,37 @@ internal class DemoMockTest {
private val demoMock = DemoMock()
class Mock {
@MockConstructor
@MockNew
private fun createBlackBox(text: String) = BlackBox("mock_$text")
@MockMethod(targetClass = DemoMock::class)
@MockInvoke(targetClass = DemoMock::class)
private fun innerFunc(text: String) = "mock_$text"
@MockMethod(targetClass = DemoMock::class)
@MockInvoke(targetClass = DemoMock::class)
private fun staticFunc(): String {
return "_MOCK_TAIL";
}
@MockMethod(targetClass = BlackBox::class)
@MockInvoke(targetClass = BlackBox::class)
private fun trim() = "trim_string"
@MockMethod(targetClass = BlackBox::class, targetMethod = "substring")
@MockInvoke(targetClass = BlackBox::class, targetMethod = "substring")
private fun sub(i: Int, j: Int) = "sub_string"
@MockMethod(targetClass = BlackBox::class)
@MockInvoke(targetClass = BlackBox::class)
private fun startsWith(s: String) = false
@MockMethod(targetClass = BlackBox::class)
@MockInvoke(targetClass = BlackBox::class)
private fun secretBox(): BlackBox {
return BlackBox("not_secret_box")
}
@MockMethod(targetClass = ColorBox::class)
@MockInvoke(targetClass = ColorBox::class)
private fun createBox(color: String, box: BlackBox): BlackBox {
return BlackBox("White_${box.get()}")
}
@MockMethod(targetClass = DemoMock::class)
@MockInvoke(targetClass = DemoMock::class)
private fun callFromDifferentMethod(): String {
return if (MOCK_CONTEXT["case"] == "special_case") {
"mock_special"
@@ -66,35 +66,35 @@ internal class DemoMockTest {
@Test
fun should_mock_new_object() {
assertEquals("mock_something", demoMock.newFunc())
verify("createBlackBox").with("something")
verifyInvoked("createBlackBox").with("something")
}
@Test
fun should_mock_member_method() {
assertEquals("{ \"res\": \"mock_hello_MOCK_TAIL\"}", demoMock.outerFunc("hello"))
verify("innerFunc").with("hello")
verify("staticFunc").with()
verifyInvoked("innerFunc").with("hello")
verifyInvoked("staticFunc").with()
}
// @Test
// fun should_mock_method_in_companion_object() {
// assertEquals("CALL_MOCK_TAIL", DemoMock.callStaticFunc())
// verify("staticFunc").with()
// verifyInvoked("staticFunc").with()
// }
@Test
fun should_mock_common_method() {
assertEquals("trim_string__sub_string__false", demoMock.commonFunc())
verify("trim").withTimes(1)
verify("sub").withTimes(1)
verify("startsWith").withTimes(1)
verifyInvoked("trim").withTimes(1)
verifyInvoked("sub").withTimes(1)
verifyInvoked("startsWith").withTimes(1)
}
@Test
fun should_mock_static_method() {
assertEquals("White_not_secret_box", demoMock.getBox().get())
verify("secretBox").withTimes(1)
verify("createBox").withTimes(1)
verifyInvoked("secretBox").withTimes(1)
verifyInvoked("createBox").withTimes(1)
}
@Test
@@ -105,7 +105,7 @@ internal class DemoMockTest {
assertEquals("mock_one_mock_others", Executors.newSingleThreadExecutor().submit<String> {
demoMock.callerOne() + "_" + demoMock.callerTwo()
}.get())
verify("callFromDifferentMethod").withTimes(4)
verifyInvoked("callFromDifferentMethod").withTimes(4)
}
@Test
@@ -117,7 +117,7 @@ internal class DemoMockTest {
assertEquals("mock_special", Executors.newSingleThreadExecutor().submit<String> {
demoMock.callerOne()
}.get())
verify("callFromDifferentMethod").withTimes(2)
verifyInvoked("callFromDifferentMethod").withTimes(2)
MOCK_CONTEXT.clear()
}
}

View File

@@ -4,7 +4,8 @@ import com.alibaba.demo.basic.model.omni.Child
import com.alibaba.demo.basic.model.omni.Parent
import com.alibaba.testable.core.tool.OmniAccessor
import com.alibaba.testable.core.tool.OmniConstructor
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertNotNull
import org.junit.jupiter.api.Test
/**
@@ -18,14 +19,14 @@ internal class DemoOmniMethodsTest {
val parent = OmniConstructor.newInstance(Parent::class.java)
// 任意深度的子孙成员对象都不为空
Assertions.assertNotNull(parent.child?.grandChild?.content)
assertNotNull(parent.child?.grandChild?.content)
// 所有基础类型初始化为默认数值
Assertions.assertEquals(0, parent.child?.grandChild?.value)
Assertions.assertEquals("", parent.child?.grandChild?.content)
assertEquals(0, parent.child?.grandChild?.value)
assertEquals("", parent.child?.grandChild?.content)
// 所有数组类型初始化为空数组
Assertions.assertEquals(0, parent.children?.size)
assertEquals(0, parent.children?.size)
}
@Test
@@ -39,23 +40,23 @@ internal class DemoOmniMethodsTest {
// 使用成员名称快速读取成员对象
var contents = OmniAccessor.get<String?>(parent, "content")
Assertions.assertEquals(4, contents.size)
Assertions.assertEquals("from child", contents[0])
Assertions.assertEquals("from 1st children", contents[1])
Assertions.assertEquals("from 2nd children", contents[2])
Assertions.assertEquals("from 3rd children", contents[3])
assertEquals(4, contents.size)
assertEquals("from child", contents[0])
assertEquals("from 1st children", contents[1])
assertEquals("from 2nd children", contents[2])
assertEquals("from 3rd children", contents[3])
// 使用成员类型快速读取成员对象
contents = OmniAccessor.get(parent, "{Child}/{GrandChild}/content")
Assertions.assertEquals(1, contents.size)
Assertions.assertEquals("from child", contents[0])
assertEquals(1, contents.size)
assertEquals("from child", contents[0])
// 使用带下标的路径读取成员对象
Assertions.assertEquals("from 2nd children", OmniAccessor.getFirst(parent, "children[1]/{GrandChild}/content"))
Assertions.assertEquals("from 3rd children", OmniAccessor.getFirst(parent, "{Child[]}[2]/{GrandChild}/content"))
assertEquals("from 2nd children", OmniAccessor.getFirst(parent, "children[1]/{GrandChild}/content"))
assertEquals("from 3rd children", OmniAccessor.getFirst(parent, "{Child[]}[2]/{GrandChild}/content"))
// 使用模糊路径快速读取成员对象
Assertions.assertEquals("from 1st children", OmniAccessor.getFirst(parent, "{C*[]}[0]/*/con*t"))
assertEquals("from 1st children", OmniAccessor.getFirst(parent, "{C*[]}[0]/*/con*t"))
}
@Test
@@ -65,22 +66,22 @@ internal class DemoOmniMethodsTest {
// 使用指定路径快速给成员对象赋值
OmniAccessor.set(parent, "child/grandChild/content", "demo child")
Assertions.assertEquals("demo child", parent.child?.grandChild?.content)
assertEquals("demo child", parent.child?.grandChild?.content)
// 使用带下标的路径给成员对象赋值
OmniAccessor.set(parent, "children[1]/grandChild/content", "demo children[1]")
Assertions.assertEquals("demo children[1]", parent.children?.get(1)?.grandChild?.content)
assertEquals("demo children[1]", parent.children?.get(1)?.grandChild?.content)
// 使用模糊路径批量给成员对象赋值
OmniAccessor.set(parent, "child*/*/content", "demo in batch")
Assertions.assertEquals("demo in batch", parent.child?.grandChild?.content)
Assertions.assertEquals("demo in batch", parent.children?.get(0)?.grandChild?.content)
Assertions.assertEquals("demo in batch", parent.children?.get(1)?.grandChild?.content)
Assertions.assertEquals("demo in batch", parent.children?.get(2)?.grandChild?.content)
assertEquals("demo in batch", parent.child?.grandChild?.content)
assertEquals("demo in batch", parent.children?.get(0)?.grandChild?.content)
assertEquals("demo in batch", parent.children?.get(1)?.grandChild?.content)
assertEquals("demo in batch", parent.children?.get(2)?.grandChild?.content)
// 读写私有内部类类型的成员(使用类型名引用内部类时,无需带外部类名)
Assertions.assertEquals("", OmniAccessor.getFirst(parent, "subChild/secret"))
assertEquals("", OmniAccessor.getFirst(parent, "subChild/secret"))
OmniAccessor.set(parent, "{InnerChild}/secret", "inner-class secret")
Assertions.assertEquals("inner-class secret", OmniAccessor.getFirst(parent, "subChild/secret"))
assertEquals("inner-class secret", OmniAccessor.getFirst(parent, "subChild/secret"))
}
}

View File

@@ -1,6 +1,6 @@
package com.alibaba.demo.basic
import com.alibaba.testable.core.tool.PrivateAccessor
import com.alibaba.testable.core.tool.PrivateAccessor.*
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
@@ -15,31 +15,31 @@ internal class DemoPrivateAccessorTest {
@Test
fun should_access_private_method() {
val list = listOf("a", "b", "c");
assertEquals("abc + hello + 1", PrivateAccessor.invoke(demoPrivateAccess, "privateFunc", list, "hello", 1))
assertEquals("abc + hello + 1", invoke(demoPrivateAccess, "privateFunc", list, "hello", 1))
}
@Test
fun should_access_private_field() {
PrivateAccessor.set(demoPrivateAccess, "count", 3)
assertEquals(3, PrivateAccessor.get(demoPrivateAccess, "count"))
set(demoPrivateAccess, "count", 3)
assertEquals(3, get(demoPrivateAccess, "count"))
}
@Test
fun should_access_private_static_method() {
val list = listOf("a", "b", "c");
assertEquals("hello + 1", PrivateAccessor.invokeStatic(DemoPrivateAccess::class.java, "privateStaticFunc", "hello", 1))
assertEquals("abc * hello * 1", PrivateAccessor.invokeStatic(DemoPrivateAccess::class.java, "privateJvmStaticFunc", list, "hello", 1))
assertEquals("hello + 1", invokeStatic(DemoPrivateAccess::class.java, "privateStaticFunc", "hello", 1))
assertEquals("abc * hello * 1", invokeStatic(DemoPrivateAccess::class.java, "privateJvmStaticFunc", list, "hello", 1))
}
@Test
fun should_access_private_static_field() {
PrivateAccessor.setStatic(DemoPrivateAccess::class.java, "staticCount", 3)
assertEquals(3, PrivateAccessor.getStatic(DemoPrivateAccess::class.java, "staticCount"))
setStatic(DemoPrivateAccess::class.java, "staticCount", 3)
assertEquals(3, getStatic(DemoPrivateAccess::class.java, "staticCount"))
}
@Test
fun should_update_final_field() {
PrivateAccessor.set(demoPrivateAccess, "pi", 4.13)
set(demoPrivateAccess, "pi", 4.13)
assertEquals(4.13, demoPrivateAccess.pi)
}

View File

@@ -1,10 +1,9 @@
package com.alibaba.demo.basic
import com.alibaba.testable.core.annotation.MockConstructor
import com.alibaba.testable.core.annotation.MockMethod
import org.junit.jupiter.api.Assertions
import com.alibaba.testable.core.annotation.MockNew
import com.alibaba.testable.core.annotation.MockInvoke
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
import java.util.*
/**
* 演示模板方法的Mock场景
@@ -15,24 +14,24 @@ internal class DemoTemplateTest {
private val demoTemplate = DemoTemplate()
class Mock {
@MockMethod
@MockInvoke
private fun <T> getList(self: DemoTemplate, value: T): List<T> {
return mutableListOf((value.toString() + "_mock_list") as T)
}
@MockMethod
@MockInvoke
private fun <K, V> getMap(self: DemoTemplate, key: K, value: V): Map<K, V> {
return mutableMapOf(key to (value.toString() + "_mock_map") as V)
}
@MockConstructor
@MockNew
private fun newHashSet(): HashSet<*> {
val set = HashSet<Any>()
set.add("insert_mock")
return set
}
@MockMethod
@MockInvoke
private fun <E> add(s: MutableSet<E>, e: E): Boolean {
s.add((e.toString() + "_mocked") as E)
return true
@@ -42,22 +41,22 @@ internal class DemoTemplateTest {
@Test
fun should_mock_single_template_method() {
val res = demoTemplate.singleTemplateMethod()
Assertions.assertEquals("demo_mock_list", res)
assertEquals("demo_mock_list", res)
}
@Test
fun should_mock_double_template_method() {
val res = demoTemplate.doubleTemplateMethod()
Assertions.assertEquals("testable_mock_map", res)
assertEquals("testable_mock_map", res)
}
@Test
fun should_mock_new_template_method() {
val res = demoTemplate.newTemplateMethod()
Assertions.assertEquals(2, res.size)
assertEquals(2, res.size)
val iterator = res.stream().iterator()
Assertions.assertEquals("insert_mock", iterator.next())
Assertions.assertEquals("world_mocked", iterator.next())
assertEquals("insert_mock", iterator.next())
assertEquals("world_mocked", iterator.next())
}
}

View File

@@ -1,14 +1,14 @@
package com.alibaba.demo.java2kotlin
import com.alibaba.testable.core.annotation.MockMethod
import com.alibaba.testable.core.matcher.InvokeVerifier.verify
import com.alibaba.testable.core.annotation.MockInvoke
import com.alibaba.testable.core.matcher.InvocationVerifier.verifyInvoked
import org.junit.jupiter.api.Test
import java.io.File
class PathDemoTest {
class Mock {
@MockMethod
@MockInvoke
fun exists(f: File): Boolean {
return when (f.absolutePath) {
"/a/b" -> true
@@ -17,7 +17,7 @@ class PathDemoTest {
}
}
@MockMethod
@MockInvoke
fun isDirectory(f: File): Boolean {
return when (f.absolutePath) {
"/a/b/c" -> true
@@ -25,12 +25,12 @@ class PathDemoTest {
}
}
@MockMethod
@MockInvoke
fun delete(f: File): Boolean {
return true
}
@MockMethod
@MockInvoke
fun listFiles(f: File): Array<File>? {
return when (f.absolutePath) {
"/a/b" -> arrayOf(File("/a/b/c"), File("/a/b/d"))
@@ -43,8 +43,8 @@ class PathDemoTest {
@Test
fun should_mock_java_method_invoke_in_kotlin() {
PathDemo.deleteRecursively(File("/a/b/"))
verify("listFiles").withTimes(2)
verify("delete").withTimes(4)
verifyInvoked("listFiles").withTimes(2)
verifyInvoked("delete").withTimes(4)
}
}

View File

@@ -1,10 +1,10 @@
package com.alibaba.demo.one2multi
import com.alibaba.testable.core.annotation.MockMethod
import com.alibaba.testable.core.annotation.MockInvoke
class ASvcMock {
@MockMethod(targetClass = String::class, targetMethod = "format")
@MockInvoke(targetClass = String::class, targetMethod = "format")
fun a_format(format: String, vararg args: Any?): String {
return "a_mock"
}

View File

@@ -1,10 +1,10 @@
package com.alibaba.demo.one2multi
import com.alibaba.testable.core.annotation.MockMethod
import com.alibaba.testable.core.annotation.MockInvoke
class BSvcMock {
@MockMethod(targetClass = String::class, targetMethod = "format")
@MockInvoke(targetClass = String::class, targetMethod = "format")
fun b_format(format: String, vararg args: Any?): String {
return "b_mock"
}

View File

@@ -1,10 +1,10 @@
package com.alibaba.demo.one2multi
import com.alibaba.testable.core.annotation.MockMethod
import com.alibaba.testable.core.annotation.MockInvoke
class CSvcMock {
@MockMethod(targetClass = String::class, targetMethod = "format")
@MockInvoke(targetClass = String::class, targetMethod = "format")
fun c_format(format: String, vararg args: Any?): String {
return "c_mock"
}

View File

@@ -1,8 +1,8 @@
package com.alibaba.demo.one2multi
import com.alibaba.testable.core.annotation.MockWith
import com.alibaba.testable.core.matcher.InvokeVerifier.verify
import org.junit.jupiter.api.Assertions
import com.alibaba.testable.core.matcher.InvocationVerifier.verifyInvoked
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
@MockWith
@@ -14,12 +14,12 @@ class OneToMultiSvcTest {
@Test
fun should_test_multi_class_together() {
Assertions.assertEquals("a_mock", aSvc.demo("test"))
Assertions.assertEquals("b_mock", bSvc.demo("test"))
Assertions.assertEquals("c_mock", cSvc.demo("test"))
verify("a_format").withTimes(1)
verify("b_format").withTimes(1)
verify("c_format").withTimes(1)
assertEquals("a_mock", aSvc.demo("test"))
assertEquals("b_mock", bSvc.demo("test"))
assertEquals("c_mock", cSvc.demo("test"))
verifyInvoked("a_format").withTimes(1)
verifyInvoked("b_format").withTimes(1)
verifyInvoked("c_format").withTimes(1)
}
}

View File

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

View File

@@ -14,8 +14,8 @@ repositories {
dependencies {
testImplementation 'org.codehaus.groovy:groovy-all:3.0.7'
testImplementation 'org.spockframework:spock-core:2.0-M5-groovy-3.0'
testImplementation('com.alibaba.testable:testable-all:0.6.4')
testAnnotationProcessor('com.alibaba.testable:testable-processor:0.6.4')
testImplementation('com.alibaba.testable:testable-all:0.6.10')
testAnnotationProcessor('com.alibaba.testable:testable-processor:0.6.10')
}
tasks.withType(JavaCompile) {

View File

@@ -12,7 +12,7 @@
<java.version>1.8</java.version>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<testable.version>0.6.4</testable.version>
<testable.version>0.6.10</testable.version>
</properties>
<dependencyManagement>

View File

@@ -1,12 +1,12 @@
package com.github.pbetkier.spockdemo
import com.alibaba.testable.core.annotation.MockConstructor
import com.alibaba.testable.core.annotation.MockMethod
import com.alibaba.testable.core.annotation.MockNew
import com.alibaba.testable.core.annotation.MockInvoke
import com.github.pbetkier.spockdemo.model.SpockBox
import spock.lang.Shared
import spock.lang.Specification
import static com.alibaba.testable.core.matcher.InvokeVerifier.verify;
import static com.alibaba.testable.core.matcher.InvocationVerifier.verifyInvoked;
class DemoSpockTest extends Specification {
@@ -14,14 +14,14 @@ class DemoSpockTest extends Specification {
def demoSpock = new DemoSpock()
static class Mock {
@MockConstructor
@MockNew
SpockBox createBox() {
SpockBox box = new SpockBox()
box.put("mock zero")
return box
}
@MockMethod(targetMethod = "put")
@MockInvoke(targetMethod = "put")
void putBox(SpockBox self, String data) {
self.put("mock " + data)
}
@@ -37,8 +37,8 @@ class DemoSpockTest extends Specification {
box.pop() == "mock 2"
box.pop() == "mock 1"
box.pop() == "mock zero"
verify("createBox").withTimes(1)
verify("putBox").withInOrder("1").withInOrder("2").withInOrder("3")
verifyInvoked("createBox").withTimes(1)
verifyInvoked("putBox").withInOrder("1").withInOrder("2").withInOrder("3")
}
}

View File

@@ -10,4 +10,4 @@ However, when the current mainstream mock framework implements the mock function
Therefore, we developed `TestableMock`, **a maverick and lightweight mock tool**.
![mock](https://testable-code.oss-cn-beijing.aliyuncs.com/en-us/mock-simpson.png)
![mock-simpson-en-us.png](https://img.alicdn.com/imgextra/i2/O1CN01CdAfqR1tP2iqFC14g_!!6000000005893-2-tps-500-761.png)

View File

@@ -19,7 +19,7 @@ Besides `TestableMock`, there are also several other community mock tools, such
`JMockit` is a mock tool whose functionality and convenience are between `Mockito` and `PowerMock`, and it makes up for their respective shortcomings. The project tried to launch a rewritten version of JMockit2 in 2017 but failed to complete, and is currently in an inactive maintenance state.
The functionality of `TestabledMock` is basically the same as that of `PowerMock`, and it is extremely easy to use. You can complete most tasks only by mastering the annotations of `@MockMethod`.
The functionality of `TestabledMock` is basically the same as that of `PowerMock`, and it is extremely easy to use. You can complete most tasks only by mastering the annotations of `@MockInvoke`.
The main disadvantage of the current `TestableMock` is that the IDE cannot promptly prompt whether the method parameters are matched correctly when writing the mock method. If the mocking effect does not meet expectation, it has to be verified during runtime through the method provided in the [self-help troubleshooting](en-us/doc/troubleshooting.md) document. This feature needs to be provided by extending IDE plugins in the future.

View File

@@ -51,12 +51,14 @@ In actual scenarios, there are very few scenarios where methods in the `String`
#### 8. Can `TestableMock` be used for testing Android projects?
It can be used in combination with [Roboelectric](https://github.com/robolectric/robolectric) testing framework.
Yes, check `demo/android-demo` project for more information.
The `Dalvik` and `ART` virtual machines of the Android system use a bytecode system different from the standard JVM, which will affect the normal functionality of `TestableMock`. The `Roboelectric` framework can run Android unit tests on a standard JVM virtual machine, which is much faster than running unit tests through the Android virtual machine. Recently, most Android App unit tests are written with the `Roboelectric` framework.
Please note, the `Dalvik` and `ART` virtual machines of the Android system use a bytecode system different from the standard JVM, which will affect the normal functionality of `TestableMock`.
If the test case require classes from Android SDK, it should be used in combination with [Roboelectric](https://github.com/robolectric/robolectric) testing framework.
This framework will run Android unit tests on a standard JVM virtual machine, which is much faster than running unit tests through the Android virtual machine, thus many Android App unit tests are written with the `Roboelectric` framework.
#### 9. Meet "Command Line is too Long. Shorten command line for ..." error when triggering test in IntelliJ IDE?
This problem is caused by the system `Class Path` content is too long, and has nothing to do with `TestableMock`. However, it should be noted that IntelliJ provides two auxiliary solutions: `JAR manifest` and `classpath file`. If `TestableMock` is used in the test, please select `JAR manifest`.
![jar-manifest](https://testable-code.oss-cn-beijing.aliyuncs.com/jar-manifest.png)
![jar-manifest.png](https://img.alicdn.com/imgextra/i2/O1CN01hfC5YE1Kw0gBIlB2x_!!6000000001227-2-tps-752-171.png)

View File

@@ -9,7 +9,7 @@ The **verifiers** and **matchers** are provided in `TestableMock` to achieve thi
@Test
public test_case() {
int res = insToTest.methodToTest();
verify("mockMethod").with(123, "abc");
verifyInvoked("mockMethod").with(123, "abc");
}
```

View File

@@ -1,5 +1,39 @@
# Release Note
## 0.7.0
- fix an improper exception issue when mock method with `associated` scope invoked
- rename type `InvokeVerifier`/`InvockeMatcher` to `InvocationVerifier`/`InvocationMatcher`
- rename method `verify` in `InvocationVerifier` type to `verifyInvoked`
- rename annotation `@MockMethod`/`@MockConstructor` to `@MockInvoke`/`@MockNew`
## 0.6.10
- support mock invocation via function reference
- support mock method defined in a base interface
## 0.6.9
- support mock invocation in lambda method
- fix a `NullPointerException` issue when `PrivateAccessor.invoke()` has `null` parameter
## 0.6.8
- support `@DumpTo` annotation to dump bytecode of any transformed class
- `PrivateAccessor.setStatic()` method now able to update static final members
## 0.6.7
- support empty mock container class which has all mock methods in its parent
- fix an issue cause mock fail when parameter has array element accessing
## 0.6.6
- support global package path mapping for easier mocking code of 3rd packages
- complete mock target checking, improve resistance to code refactoring
- fix a log disorder issue of mock class handler
- fix an error of creating `Long` or `Integer` class with `OmniConstructor` in certain jvm version
## 0.6.5
- fix an issue cause `OmniConstructor` fail to handle classes like Date and BigDecimal
- fix an issue of `PrivateAccessor` fail to invoke method with single array parameter
- fix an issue of mock methods in super class are ignored when `scope` is `associated`
- fix an issue of empty mock method not works when `scope` is `associated`
## 0.6.4
- `TestableNull` class removed, let `OmniConstructor` even lighter
- support `@Nested` annotation of JUnit 5
@@ -38,6 +72,13 @@
- fix an exception caused by method parameter with ternary operator
- fix a bug cause log message lost when `@MockWith` annotation used
## 0.5.0
- split test class and mock class, let the mock class and method reusable
- support use mock when package path of the test class is different from the class under test
- support limit the mock effective scope to the cases of class under test it bound
- use `TransmittableThreadLocal` to store mock context
- add annotation `@MockDiagnose` to print debug logs
## 0.4.12
- support verbose diagnose log for better self-troubleshooting
- support disable private access target existence check

View File

@@ -6,7 +6,7 @@ Use TestableMock
- [Quickly mock arbitrary call](en-us/doc/use-mock.md): quickly replace any method invocation in the class under test with a mock method, solve the cumbersome use of traditional mock tools problem
- [Access private members of the class under test](en-us/doc/private-accessor.md): enable unit tests directly invoke or access private members of the class under test, solve the problems of private member initialization and private method testing
- [Quickly construct complicated parameter object](en-us/doc/omni-constructor.md)generate arbitrarily nested object instances, simplify their internal member assignment methods, solve the problem of long initialization codes for method parameters
- [Auxiliary test void method](en-us/doc/test-void-method.md): use the mock validator to check the internal logic of method, solve the problem that unit testing is difficult to implement to the method with no return value
- [Assist test void method](en-us/doc/test-void-method.md): use the mock validator to check the internal logic of method, solve the problem that unit testing is difficult to implement to the method with no return value
## Use in Maven project
@@ -16,7 +16,7 @@ It is recommended to add a `property` field that identifies the TestableMock ver
```xml
<properties>
<testable.version>0.6.4</testable.version>
<testable.version>0.6.10</testable.version>
</properties>
```
@@ -63,8 +63,8 @@ Add dependence of `TestableMock` in `build.gradle` file:
```groovy
dependencies {
testImplementation('com.alibaba.testable:testable-all:0.6.4')
testAnnotationProcessor('com.alibaba.testable:testable-processor:0.6.4')
testImplementation('com.alibaba.testable:testable-all:0.6.10')
testAnnotationProcessor('com.alibaba.testable:testable-processor:0.6.10')
}
```
@@ -78,7 +78,7 @@ test {
See the [build.gradle](https://github.com/alibaba/testable-mock/blob/master/demo/java-demo/build.gradle) file of project `java-demo` and the [build.gradle.kts](https://github.com/alibaba/testable-mock/blob/master/demo/kotlin-demo/build.gradle.kts) file of project `kotlin-demo`.
> For Android project tested with `Robolectric` framework, please use the same method to add `TestableMock` dependency as above, and add `javaagent` configuration as follows:
> For Android project, please use the same method to add `TestableMock` dependency as above, and add `javaagent` configuration as follows:
>
> ```groovy
> android {
@@ -92,13 +92,14 @@ See the [build.gradle](https://github.com/alibaba/testable-mock/blob/master/demo
> }
> ```
>
> See [issue-43](https://github.com/alibaba/testable-mock/issues/43) for a complete example.
> See project `demo/android-demo` for a complete example.
> If the project is using `Spock` test framework, you need to specify the bytecode version generated by `Groovy` to be 1.6 or above, the method is as follows (please modify the properties value according to the actual JVM version used).
>
> For Maven project, add `<maven.compiler.source>` and `<maven.compiler.target>` properties inside the `pom.xml` file, e.g.
> ```xml
> <properties>
> <!-- or 1.7/1.8/... -->
> <maven.compiler.source>1.6</maven.compiler.source>
> <maven.compiler.target>1.6</maven.compiler.target>
> </properties>
@@ -106,7 +107,7 @@ See the [build.gradle](https://github.com/alibaba/testable-mock/blob/master/demo
>
> For Gradle project, add a `sourceCompatibility` property inside the `build.gradle` file, e.g.
> ```groovy
> sourceCompatibility = '6'
> sourceCompatibility = '6' // or 7/8/9/...
> ```
>
> See project `demo/spock-demo` for a complete example.

View File

@@ -70,7 +70,7 @@ class Demo {
To test this method, you can use `TestableMock` to quickly mock out the `System.out.println` method. In the mock method body, you can simply call the original method (equivalent to not affecting the original method function, only used for call recording), or leave it blank (equivalent to removing the side effects of the original method).
After executing the void type method under test, use `InvokeVerifier.verify()` to verify whether the incoming print content meets expectations:
After executing the void type method under test, use `InvocationVerifier.verifyInvoked()` to verify whether the incoming print content meets expectations:
```java
class DemoTest {
@@ -78,7 +78,7 @@ class DemoTest {
public static class Mock {
// Intercept `System.out.println` invocation
@MockMethod
@MockInvoke
public void println(PrintStream ps, String msg) {
// Execute the original call
ps.println(msg);
@@ -90,7 +90,7 @@ class DemoTest {
Action action = new Action("click", ":download");
demo.recordAction();
// Verify mock method `println` is invoked, and passing parameters in line with expectations
verify("println").with(matches("\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2} \\[click\\] :download"));
verifyInvoked("println").with(matches("\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2} \\[click\\] :download"));
}
}
```

View File

@@ -39,7 +39,7 @@ public class DemoMockTest {
@Test
void should_mock_member_method() throws Exception {
assertEquals("hello_world", demoMock.outerFunc());
verify("innerFunc").with("world");
verifyInvoked("innerFunc").with("world");
}
}
```
@@ -59,7 +59,7 @@ public class DemoMockTest {
@Test
void should_mock_member_method() throws Exception {
assertEquals("hello_world", demoMock.outerFunc());
verify("innerFunc").with("world");
verifyInvoked("innerFunc").with("world");
}
}
```

View File

@@ -15,4 +15,4 @@ At the same time, because the built-in unit test executor of `Eclipse` completel
Take the use of `JUnit` as an example. You need to pull down from the small triangle next to the run button on the IDE toolbar, select "Run Configurations...", select the task to run the unit test on the left side, and switch to "arguments" Tab on the right side, append a `-javaagent:` parameter in the "VM Options", the following figure is an example, note that the `testable-agent` package should be modified to match the actual situation of the local Maven repository path.
![eclipse-junit-configuration](https://testable-code.oss-cn-beijing.aliyuncs.com/eclipse-junit-configuration.png)
![eclipse-junit-configuration.png](https://img.alicdn.com/imgextra/i3/O1CN01C7DwGs1dHgVRAhh3y_!!6000000003711-2-tps-1430-1004.png)

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