diff --git a/UnitAuto-Admin/apijson/CodeUtil.js b/UnitAuto-Admin/apijson/CodeUtil.js
index d8b2299..9472f68 100644
--- a/UnitAuto-Admin/apijson/CodeUtil.js
+++ b/UnitAuto-Admin/apijson/CodeUtil.js
@@ -6036,6 +6036,7 @@ var CodeUtil = {
return valuesIsNotString ? ' ! value必须是String类型!且必须符合 countArray 这种方法名格式!' : (isWarning ? '' : CodeUtil.getComment('被调用方法名', false, ' '));
case 'constructor':
return valuesIsNotString ? ' ! value必须是String类型!且必须符合 getInstance 这种方法名格式!' : (isWarning ? '' : CodeUtil.getComment('获取类实例的方法名,一般用于单例模式类', false, ' '));
+ case 'args':
case 'methodArgs':
case 'classArgs':
if (value == null || value instanceof Array) {
@@ -6535,6 +6536,7 @@ var CodeUtil = {
if (CodeUtil.getType4Request(value) == 'string') {
return isWarning ? '' : '获取类实例的方法名,一般用于单例模式类';
}
+ case 'args':
case 'methodArgs':
case 'classArgs':
if (value == null || value instanceof Array) {
diff --git a/UnitAuto-Admin/index.html b/UnitAuto-Admin/index.html
index 4dcb39a..6e6ea51 100755
--- a/UnitAuto-Admin/index.html
+++ b/UnitAuto-Admin/index.html
@@ -315,11 +315,11 @@
{
"static": true,
"methodArgs": [
- { // 可省略来自动判断的 type: Boolean,Integer,BigDecimal,String,JSONObject,JSONArray
+ { // 可省略来自动判断的 type: Boolean,Integer,BigDecimal,String,JSONObject,JSONArray
"type": "long",
"value": 1
},
- { // 这个对象还可以简化为 "int:2" 这种字符串形式,方便手动调试。在键值对后按 Enter 回车键自动智能生成补全提示
+ { // 这个对象还可以简化为 "int:2" 这种字符串形式,方便手动调试。在键值对后按 Enter 回车键自动智能生成补全提示
"type": "long",
"value": 2
}
@@ -978,14 +978,25 @@
var vRemember = document.getElementById("vRemember");
var lang = App == null ? null : App.language;
- vUrl.value = lang == CodeUtil.LANGUAGE_GO ? 'test.Divide.Divide' : 'unitauto.test.TestUtil.divide' //main.js里访问不到,可能是script引用顺序问题
+ vUrl.value = lang == CodeUtil.LANGUAGE_GO ? 'test.Divide' : (lang == CodeUtil.LANGUAGE_PYTHON ? 'unitauto.test.testutil.divide' : 'unitauto.test.TestUtil.divide') //main.js里访问不到,可能是script引用顺序问题
vInput.value = (lang == CodeUtil.LANGUAGE_GO ? `{
"methodArgs": [
- { // 可省略来自动判断的 type: bool,float64,string,map[string]any,[]any
+ { // 可省略来自动判断的 type: bool,float64,string,map[string]any,[]any
"type": "int",
"value": 1
},
- { // 这个对象还可以简化为 "int:2" 这种字符串形式,方便手动调试。在键值对后按 Enter 回车键自动智能生成补全提示
+ { // 这个对象还可以简化为 "int:2" 这种字符串形式,方便手动调试。在键值对后按 Enter 回车键自动智能生成补全提示
+ "type": "int",
+ "value": 2
+ }
+ ]
+}` : (lang == CodeUtil.LANGUAGE_PYTHON ? `{
+ "args": [
+ { // 可省略来自动判断的 type: bool,int,float,str,dict,list
+ "type": "int",
+ "value": 1
+ },
+ { // 这个对象还可以简化为 "int:2" 这种字符串形式,方便手动调试。在键值对后按 Enter 回车键自动智能生成补全提示
"type": "int",
"value": 2
}
@@ -993,16 +1004,16 @@
}` : `{
"static": true,
"methodArgs": [
- { // 可省略来自动判断的 type: Boolean,Integer,BigDecimal,String,JSONObject,JSONArray
+ { // 可省略来自动判断的 type: Boolean,Integer,BigDecimal,String,JSONObject,JSONArray
"type": "long",
"value": 1
},
- { // 这个对象还可以简化为 "int:2" 这种字符串形式,方便手动调试。在键值对后按 Enter 回车键自动智能生成补全提示
+ { // 这个对象还可以简化为 "int:2" 这种字符串形式,方便手动调试。在键值对后按 Enter 回车键自动智能生成补全提示
"type": "long",
"value": 2
}
]
-}`) + `
+}`)) + `
/*
以上 JSON 文本支持 JSON5 格式。清空文本内容可查看规则。
diff --git a/UnitAuto-Go/README.md b/UnitAuto-Go/README.md
index c38cd95..96baf52 100644
--- a/UnitAuto-Go/README.md
+++ b/UnitAuto-Go/README.md
@@ -9,6 +9,8 @@ UnitAuto Go Library for remote dependencies with GitHub repo, etc.
+**Demo:** https://github.com/TommyLemon/unitauto-go-demo
+
#### 1. 在 go.mod 中添加 GitHub 仓库
#### 1. Add the GitHub repository to go.mod
```go
@@ -42,3 +44,32 @@ https://github.com/TommyLemon/unitauto-go/blob/main/main.go
+
+
+### 4. 关于作者
+### 4. Author
+[https://github.com/TommyLemon](https://github.com/TommyLemon)
+
+
+如果有什么问题或建议可以 [去 APIAuto 提 issue](https://github.com/TommyLemon/APIAuto/issues),交流技术,分享经验。
+如果你解决了某些 bug,或者新增了一些功能,欢迎 [提 PR 贡献代码](https://github.com/Tencent/APIJSON/blob/master/CONTRIBUTING.md),感激不尽。
+
+If you have any questions or suggestions, you can [create an issue](https://github.com/TommyLemon/APIAuto/issues).
+If you can added a feature or fixed a bug, please [create a pull request](https://github.com/TommyLemon/unitauto-go/pulls), thank you~
+
+
+### 5. 其它项目
+### 5. Link
+创作不易、坚持更难,右上角点 ⭐ Star 支持下吧,谢谢 ^_^
+
+[UnitAuto](https://github.com/TommyLemon/UnitAuto) 机器学习零代码单元测试平台,零代码、全方位、自动化 测试 方法/函数 的正确性、可用性和性能
+
+[unitauto-go-demo](https://github.com/TommyLemon/unitauto-go-demo) UnitAuto Go Demo,提供用来做单元测试的业务函数
+
+[unitauto-py](https://github.com/TommyLemon/unitauto-py) UnitAuto Python 库,可通过 pip 仓库等远程依赖
+
+[APIJSON](https://github.com/Tencent/APIJSON) 🚀 腾讯零代码、全功能、强安全 ORM 库 🏆 后端接口和文档零代码,前端(客户端) 定制返回 JSON 的数据和结构
+
+[APIAuto](https://github.com/TommyLemon/APIAuto) 敏捷开发最强大易用的 HTTP 接口工具,机器学习零代码测试、生成代码与静态检查、生成文档与光标悬浮注释,集 文档、测试、Mock、调试、管理 于一体的一站式体验
+
+[SQLAuto](https://github.com/TommyLemon/SQLAuto) 智能零代码自动化测试 SQL 语句执行结果的数据库工具,任意增删改查、任意 SQL 模板变量、一键批量生成参数组合、快速构造大量测试数据
diff --git a/UnitAuto-Go/unitauto/method_util.go b/UnitAuto-Go/unitauto/method_util.go
index d4dc4f4..05f0e46 100644
--- a/UnitAuto-Go/unitauto/method_util.go
+++ b/UnitAuto-Go/unitauto/method_util.go
@@ -76,6 +76,7 @@ var KEY_RETURN = "return"
var KEY_TIME_DETAIL = "time:start|duration|end"
var KEY_CLASS_ARGS = "classArgs"
var KEY_METHOD_ARGS = "methodArgs"
+var KEY_ARGS = "args"
var KEY_CALLBACK = "callback"
var KEY_GLOBAL = "global"
@@ -565,6 +566,16 @@ func InvokeMethod(req map[string]any, instance any, listener Listener[any]) erro
var this_ = GetMap(req, KEY_THIS)
var clsArgs = GetArgList(req, KEY_CLASS_ARGS)
var methodArgs = GetArgList(req, KEY_METHOD_ARGS)
+ var args = GetArgList(req, KEY_ARGS)
+ if len(args) > 0 {
+ if methodArgs != nil {
+ err := errors.New(KEY_ARGS + " 和 " + KEY_METHOD_ARGS + " 不能同时传!")
+ completeWithError(pkgName, clsName, methodName, startTime, err, listener)
+ return err
+ }
+
+ methodArgs = args
+ }
if IsEmpty(cttName, true) && len(clsArgs) > 0 {
err := errors.New("Go 没有构造函数,不允许单独传 " + KEY_CLASS_ARGS + " ,必须配合 " + KEY_CONSTRUCTOR + " 一起用!")
diff --git a/UnitAuto-Python/README.md b/UnitAuto-Python/README.md
index 7f950f2..e3b8b41 100644
--- a/UnitAuto-Python/README.md
+++ b/UnitAuto-Python/README.md
@@ -54,3 +54,32 @@ if you cannot run the command successfully, try python3:
#### 3. Test by following the main repo
https://github.com/TommyLemon/UnitAuto
+
+
+
+### 4. 关于作者
+### 4. Author
+[https://github.com/TommyLemon](https://github.com/TommyLemon)
+
+
+如果有什么问题或建议可以 [去 APIAuto 提 issue](https://github.com/TommyLemon/APIAuto/issues),交流技术,分享经验。
+如果你解决了某些 bug,或者新增了一些功能,欢迎 [提 PR 贡献代码](https://github.com/Tencent/APIJSON/blob/master/CONTRIBUTING.md),感激不尽。
+
+If you have any questions or suggestions, you can [create an issue](https://github.com/TommyLemon/APIAuto/issues).
+If you can added a feature or fixed a bug, please [create a pull request](https://github.com/TommyLemon/unitauto-py/pulls), thank you~
+
+
+
+### 5. 其它项目
+### 5. Link
+创作不易、坚持更难,右上角点 ⭐ Star 支持下吧,谢谢 ^_^
+
+[UnitAuto](https://github.com/TommyLemon/UnitAuto) 机器学习零代码单元测试平台,零代码、全方位、自动化 测试 方法/函数 的正确性、可用性和性能
+
+[unitauto-go](https://github.com/TommyLemon/unitauto-go) UnitAuto Go 库,可通过 GitHub 仓库等远程依赖
+
+[APIJSON](https://github.com/Tencent/APIJSON) 🚀 腾讯零代码、全功能、强安全 ORM 库 🏆 后端接口和文档零代码,前端(客户端) 定制返回 JSON 的数据和结构
+
+[APIAuto](https://github.com/TommyLemon/APIAuto) 敏捷开发最强大易用的 HTTP 接口工具,机器学习零代码测试、生成代码与静态检查、生成文档与光标悬浮注释,集 文档、测试、Mock、调试、管理 于一体的一站式体验
+
+[SQLAuto](https://github.com/TommyLemon/SQLAuto) 智能零代码自动化测试 SQL 语句执行结果的数据库工具,任意增删改查、任意 SQL 模板变量、一键批量生成参数组合、快速构造大量测试数据
diff --git a/UnitAuto-Python/main.py b/UnitAuto-Python/main.py
index 9f7ceb2..8120315 100644
--- a/UnitAuto-Python/main.py
+++ b/UnitAuto-Python/main.py
@@ -6,6 +6,7 @@
def callback():
pass
+ # methodutil.config.DEFAULT_MODULE_PATH = "unitauto"
# methodutil.listener.callback = callback
test()
diff --git a/UnitAuto-Python/unitauto/methodutil.py b/UnitAuto-Python/unitauto/methodutil.py
index a4bc7b3..ecec893 100644
--- a/UnitAuto-Python/unitauto/methodutil.py
+++ b/UnitAuto-Python/unitauto/methodutil.py
@@ -87,9 +87,19 @@
MILLIS_TIME = 1000000
PATTERN_ALPHABET = re.compile('^[A-Za-z]+$')
+PATTERN_UPPER_ALPHABET = re.compile('^[A-Z]+$')
+PATTERN_LOWER_ALPHABET = re.compile('^[a-z]+$')
PATTERN_NUMBER = re.compile('^[0-9]+$')
PATTERN_NAME = re.compile('^[A-Za-z0-9_]+$')
+
+class Config:
+ DEFAULT_MODULE_PATH: str = ''
+
+
+config = Config()
+
+
PRIMITIVE_CLASS_MAP = {
None: any,
'None': any,
@@ -339,7 +349,7 @@ def list_method(req, import_fun: callable = null) -> dict:
if is_file:
continue
- mtd = parse_method(cls) if is_all_mtd or cls.__name__ == method else null
+ mtd = parse_method(cls, import_fun=import_fun) if is_all_mtd or cls.__name__ == method else null
if is_empty(mtd):
continue
@@ -413,7 +423,7 @@ def list_method(req, import_fun: callable = null) -> dict:
mtd_list = []
for mtd in ml:
- m = parse_method(mtd)
+ m = parse_method(mtd, import_fun=import_fun)
if not_empty(m):
mtd_list.append(m)
@@ -462,21 +472,68 @@ def list_method(req, import_fun: callable = null) -> dict:
}
-def parse_method(func) -> dict:
+def get_type_str_by_str(s: str, keep_prefix: bool = false, import_fun: callable = null) -> str:
+ start = s.index("'") if is_contain(s, "'") else -1
+ if start >= 0:
+ s = s[start + 1:]
+ end = s.index("'") if is_contain(s, "'") else -1
+ if end >= 0:
+ s = s[:end]
+ s = s.strip()
+
+ if s in (null, '', '_empty', 'inspect._empty', 'NoneType', 'POSITIONAL_OR_KEYWORD'):
+ return null
+
+ dmp = null if keep_prefix else config.DEFAULT_MODULE_PATH
+ if not_empty(dmp) and s.startswith(dmp):
+ s = s[len(dmp) + 1:]
+
+ # if s.startswith('__init__.'):
+ # return s[len('__init__.'):]
+
+ s = s.replace('.__init__.', '$')
+ if '$' not in s and '.' in s:
+ ks = split(s, '.')
+ # util.Test.Test2 会误判为 util.Test$Test2,实际应该为 util$Test.Test2 s = '.'.join(ks[:-1]) + '$' + ks[-1]
+
+ ns = ks[0]
+ ks = ks[1:]
+ is_inner_cls = false
+ for k in ks:
+ if (not is_inner_cls) and not is_module_path(ns + '.' + k, import_fun): # is_big_name(k)
+ is_inner_cls = true
+
+ ns += ('$' if is_inner_cls else '.') + k
+
+ return ns
+
+ return s
+
+
+def is_module_path(path: str, import_fun: callable = null) -> bool:
+ try:
+ import_fun = import_fun or __import__
+ m = import_fun(path)
+ return not_none(m)
+ except Exception as e:
+ print(e)
+ return false
+
+
+def get_type_str(return_annotation, instance: any = null, keep_prefix: bool = false, import_fun: callable = null) -> str:
+ rt = get_type_str_by_str(str(return_annotation), keep_prefix=keep_prefix, import_fun=import_fun)
+ if is_empty(rt):
+ rt = get_type_str_by_str(str(type(instance)), keep_prefix=keep_prefix, import_fun=import_fun)
+ return rt
+
+
+def parse_method(func, import_fun: callable = null) -> dict:
signature = null if func is None else inspect.signature(func)
if signature is None:
return {}
return_annotation = signature.return_annotation
- rt = null
- if return_annotation is not None:
- try:
- rt = return_annotation.__name__
- except Exception as e:
- rt = str(return_annotation)
-
- if rt in ('_empty', 'POSITIONAL_OR_KEYWORD'):
- rt = null
+ rt = get_type_str(return_annotation, keep_prefix=true, import_fun=import_fun)
types = []
for param in signature.parameters.values():
@@ -508,21 +565,13 @@ def parse_method(func) -> dict:
def wrap_result(
instance, func, method_args, ma_types, ma_values, result, start_time,
- json_dumps: callable = null, json_loads: callable = null
+ json_dumps: callable = null, json_loads: callable = null, import_fun: callable = null
):
time_detail = get_time_detail(start_time)
signature = inspect.signature(func)
return_annotation = null if signature is None else signature.return_annotation
- rt = null
- if return_annotation is not None:
- try:
- rt = return_annotation.__name__
- if rt in ('_empty', 'POSITIONAL_OR_KEYWORD'):
- rt = type(result).__name__
- except Exception as e:
- print(e)
- rt = type(result).__name__ # str(return_annotation)
+ rt = get_type_str(return_annotation, result, import_fun=import_fun)
mal = size(method_args)
mas = [null] * mal
@@ -551,6 +600,16 @@ def wrap_result(
}
if is_none(instance):
+ if result is None and is_empty(rt):
+ return {
+ KEY_LANGUAGE: LANGUAGE,
+ KEY_OK: true,
+ KEY_CODE: CODE_SUCCESS,
+ KEY_MSG: MSG_SUCCESS,
+ KEY_METHOD_ARGS: mas,
+ KEY_TIME_DETAIL: time_detail
+ }
+
return {
KEY_LANGUAGE: LANGUAGE,
KEY_OK: true,
@@ -584,6 +643,17 @@ def wrap_result(
this[KEY_VALUE] = str(instance)
this[KEY_WARN] = str(e)
+ if result is None and is_empty(rt):
+ return {
+ KEY_LANGUAGE: LANGUAGE,
+ KEY_OK: true,
+ KEY_CODE: CODE_SUCCESS,
+ KEY_MSG: MSG_SUCCESS,
+ KEY_METHOD_ARGS: mas,
+ KEY_THIS: this,
+ KEY_TIME_DETAIL: time_detail
+ }
+
return {
KEY_LANGUAGE: LANGUAGE,
KEY_OK: true,
@@ -678,7 +748,10 @@ def invoke_method(
def final_callback(*args, **kwargs):
if not_none(callback): # callable(callback):
- callback(wrap_result(final_result.get(KEY_THIS), func, method_args, ma_types, ma_values, final_result.get(KEY_VALUE), start_time))
+ callback(wrap_result(
+ final_result.get(KEY_THIS), func, method_args, ma_types, ma_values,
+ final_result.get(KEY_VALUE), start_time, import_fun=import_fun
+ ))
is_wait = init_args(method_args, ma_keys, ma_types, ma_values, m_kwargs, true, final_callback, import_fun=import_fun)
@@ -691,11 +764,19 @@ def final_callback(*args, **kwargs):
cls: any = null
if l > 1:
i = -1
+ m = module
for n in fl:
i += 1
if i <= 0:
continue
- cls = getattr(module, n)
+ # if i > 1:
+ # n = 'Test$InnerTest' # 'Test.InnerTest'
+ # # 'Test' object has no attribute 'InnerTest' # m = m()
+ m = getattr(m, n) # FIXME Test.InnerTest raise Exception: type object 'Test' has no attribute 'InnerTest'
+
+ cls = m
+ # 'testutil' object has no attribute 'Test.InnerTest' cls = getattr(module, '.'.join(fl[1:]))
+ # 'testutil' object has no attribute 'Test.InnerTest' cls = getattr(module, '$'.join(fl[1:]))
if cls is None:
func = getattr(module, method)
@@ -724,7 +805,7 @@ def final_callback(*args, **kwargs):
final_result[KEY_VALUE] = result
res = wrap_result(
instance, func, method_args, ma_types, ma_values, result, start_time,
- json_dumps=json_dumps, json_loads=json_loads
+ json_dumps=json_dumps, json_loads=json_loads, import_fun=import_fun
)
except Exception as e:
res = {
@@ -1017,6 +1098,12 @@ def is_name(s: str) -> bool:
return not_none(PATTERN_NAME.match(s))
+def is_big_name(s: str) -> bool:
+ if is_empty(s) or not PATTERN_UPPER_ALPHABET.match(s[:1]):
+ return false
+ return not_none(PATTERN_NAME.match(s))
+
+
def size(obj) -> int:
if obj is None:
return 0
diff --git a/UnitAuto-Python/unitauto/test/testutil.py b/UnitAuto-Python/unitauto/test/testutil.py
index 257d75a..ff3f96f 100644
--- a/UnitAuto-Python/unitauto/test/testutil.py
+++ b/UnitAuto-Python/unitauto/test/testutil.py
@@ -136,6 +136,18 @@ def is_female(self) -> bool:
def get_sex_str(self) -> str:
return 'Male' if self.is_male() else 'Female'
+ class InnerTest:
+ id: int = 0
+ name: str = 'InnerTest'
+
+ def __init__(self, id: int = 0, name: str = ''):
+ self.id = id
+ self.name = name
+
+ @staticmethod
+ def get_instance(id: int = 0, name: str = '') -> 'Test.InnerTest':
+ return Test.InnerTest(id=id, name=name)
+
def get_test_instance(id: int = 0, sex: int = 0, name: str = '') -> Test:
return Test(id=id, sex=sex, name=name)