mirror of
https://github.com/alibaba/testable-mock.git
synced 2025-01-09 20:00:21 +08:00
implement fall-fast anti-refactor machenism
This commit is contained in:
parent
3d6d3c9dd1
commit
27174afa93
@ -103,9 +103,9 @@ OmniAccessor.set(parent, "children[2]/*/value", 100);
|
||||
|
||||
> **你真的需要用到`OmniAccessor`吗?**
|
||||
>
|
||||
> `OmniAccessor`具有基于Fail-Fast机制的防代码重构能力,当用户提供的访问路径无法匹配到任何成员时,`OmniAccessor`将立即抛出异常,使单元测试提前终止。然而相比常规的成员访问方式,`OmniAccessor`在IDE重构方面的支持依然偏弱。
|
||||
> `OmniAccessor`具有基于Fail-Fast机制的防代码重构能力,当用户提供的访问路径无法匹配到任何成员时,`OmniAccessor`将立即抛出`NoSuchMemberError`错误,使单元测试提前终止。然而相比常规的成员访问方式,`OmniAccessor`在IDE重构方面的支持依然偏弱。
|
||||
>
|
||||
> 对于复杂对象的内容赋值,大多数情况下,我们更推荐使用[构造者模式](https://developer.aliyun.com/article/705058),或者暴露Getter/Setter方法实现。这些常规手段虽然稍显笨拙(尤其在需要为许多相似的成员批量赋值的时候),但对业务逻辑的封装和重构都更加友好。
|
||||
> 仅当原类型不适合改造,且没有其它可访问目标成员的方法时,`OmniAccessor`才是最后的终极工具。
|
||||
> 仅当原类型不适合改造,且没有其它可访问目标成员的方法时,`OmniAccessor`才是最后的终极手段。
|
||||
>
|
||||
> 出于相同的原因,虽然技术上可行,但我们并不推荐在除单元测试之外的场景使用`OmniAccessor`方式来读写业务类的成员字段。
|
||||
> 出于相同的原因,我们并不推荐在除单元测试之外的场景使用`OmniAccessor`方式来读写业务类的成员字段(虽然技术上可行)。
|
||||
|
@ -0,0 +1,12 @@
|
||||
package com.alibaba.testable.core.error;
|
||||
|
||||
/**
|
||||
* @author flin
|
||||
*/
|
||||
public class NoSuchMemberError extends Error {
|
||||
|
||||
public NoSuchMemberError(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
package com.alibaba.testable.core.tool;
|
||||
|
||||
import com.alibaba.testable.core.error.NoSuchMemberError;
|
||||
import com.alibaba.testable.core.util.CollectionUtil;
|
||||
import com.alibaba.testable.core.util.FixSizeMap;
|
||||
import com.alibaba.testable.core.util.TypeUtil;
|
||||
@ -67,6 +68,9 @@ public class OmniAccessor {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (values.isEmpty()) {
|
||||
throw new NoSuchMemberError("Query \"" + queryPath + "\"" + " does not match any member!");
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
@ -86,8 +90,9 @@ public class OmniAccessor {
|
||||
List<Object> parent = getByPath(target, toParent(memberPath), toParent(queryPath));
|
||||
if (!parent.isEmpty()) {
|
||||
for (Object p : parent) {
|
||||
setByPathSegment(p, toChild(memberPath), toChild(queryPath), value);
|
||||
count++;
|
||||
if (setByPathSegment(p, toChild(memberPath), toChild(queryPath), value)) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (NoSuchFieldException e) {
|
||||
@ -97,6 +102,9 @@ public class OmniAccessor {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (count == 0) {
|
||||
throw new NoSuchMemberError("Query \"" + queryPath + "\"" + " does not match any member!");
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
@ -237,21 +245,26 @@ public class OmniAccessor {
|
||||
return fullQuerySegments;
|
||||
}
|
||||
|
||||
private static void setByPathSegment(Object target, String memberSegment, String querySegment, Object value)
|
||||
private static boolean setByPathSegment(Object target, String memberSegment, String querySegment, Object value)
|
||||
throws IllegalAccessException {
|
||||
String name = extraNameFromMemberRecord(memberSegment);
|
||||
int nth = extraIndexFromQuery(querySegment);
|
||||
boolean isFieldMatch = false;
|
||||
if (target.getClass().isArray()) {
|
||||
for (int i = 0; i < Array.getLength(target); i++) {
|
||||
setFieldByName(Array.get(target, i), name, nth, value);
|
||||
isFieldMatch |= setFieldByName(Array.get(target, i), name, nth, value);
|
||||
}
|
||||
} else {
|
||||
setFieldByName(target, name, nth, value);
|
||||
isFieldMatch = setFieldByName(target, name, nth, value);
|
||||
}
|
||||
return isFieldMatch;
|
||||
}
|
||||
|
||||
private static void setFieldByName(Object target, String name, int nth, Object value) throws IllegalAccessException {
|
||||
private static boolean setFieldByName(Object target, String name, int nth, Object value) throws IllegalAccessException {
|
||||
Field field = TypeUtil.getFieldByName(target.getClass(), name);
|
||||
if (field == null) {
|
||||
return false;
|
||||
}
|
||||
field.setAccessible(true);
|
||||
if (field.getType().isArray()) {
|
||||
Object f = field.get(target);
|
||||
@ -265,6 +278,7 @@ public class OmniAccessor {
|
||||
} else {
|
||||
field.set(target, value);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user