diff --git a/demo/java-demo/src/test/java/com/alibaba/testable/demo/service/DemoPrivateAccessServiceTest.java b/demo/java-demo/src/test/java/com/alibaba/testable/demo/service/DemoPrivateAccessServiceTest.java index 0bb3d7f..f8c5b78 100644 --- a/demo/java-demo/src/test/java/com/alibaba/testable/demo/service/DemoPrivateAccessServiceTest.java +++ b/demo/java-demo/src/test/java/com/alibaba/testable/demo/service/DemoPrivateAccessServiceTest.java @@ -12,15 +12,17 @@ class DemoPrivateAccessServiceTest { private DemoPrivateAccessService demoService = new DemoPrivateAccessService(); @Test - void should_able_to_mock_private_method() throws Exception { + void should_able_to_access_private_method() throws Exception { assertEquals("hello - 1", demoService.privateFunc("hello", 1)); assertEquals("hello - 1", PrivateAccessor.invoke(demoService, "privateFunc", "hello", 1)); } @Test - void should_able_to_mock_private_field() throws Exception { + void should_able_to_access_private_field() throws Exception { demoService.count = 2; assertEquals("4", demoService.privateFieldAccessFunc()); + assertEquals(new Integer(4), demoService.count); + PrivateAccessor.set(demoService, "count", 3); assertEquals("5", demoService.privateFieldAccessFunc()); assertEquals(new Integer(5), PrivateAccessor.get(demoService, "count")); diff --git a/demo/kotlin-demo/src/test/kotlin/com/alibaba/testable/demo/service/DemoPrivateAccessServiceTest.kt b/demo/kotlin-demo/src/test/kotlin/com/alibaba/testable/demo/service/DemoPrivateAccessServiceTest.kt index 4192139..d420599 100644 --- a/demo/kotlin-demo/src/test/kotlin/com/alibaba/testable/demo/service/DemoPrivateAccessServiceTest.kt +++ b/demo/kotlin-demo/src/test/kotlin/com/alibaba/testable/demo/service/DemoPrivateAccessServiceTest.kt @@ -10,12 +10,12 @@ internal class DemoPrivateAccessServiceTest { private val demoService = DemoPrivateAccessService() @Test - fun should_able_to_mock_private_method() { + fun should_able_to_access_private_method() { assertEquals("hello - 1", PrivateAccessor.invoke(demoService, "privateFunc", "hello", 1)) } @Test - fun should_able_to_mock_private_field() { + fun should_able_to_access_private_field() { PrivateAccessor.set(demoService, "count", 3) assertEquals("5", demoService.privateFieldAccessFunc()) assertEquals(5, PrivateAccessor.get(demoService, "count")) diff --git a/testable-processor/src/main/java/com/alibaba/testable/processor/generator/PrivateAccessStatementGenerator.java b/testable-processor/src/main/java/com/alibaba/testable/processor/generator/PrivateAccessStatementGenerator.java index 7dd9dd8..1655629 100644 --- a/testable-processor/src/main/java/com/alibaba/testable/processor/generator/PrivateAccessStatementGenerator.java +++ b/testable-processor/src/main/java/com/alibaba/testable/processor/generator/PrivateAccessStatementGenerator.java @@ -15,6 +15,13 @@ public class PrivateAccessStatementGenerator extends BaseGenerator { super(cx); } + public JCExpression fetchGetterStatement(JCFieldAccess access) { + JCFieldAccess getter = cx.treeMaker.Select(nameToExpression(ConstPool.TESTABLE_PRIVATE_ACCESSOR), + cx.names.fromString("get")); + return cx.treeMaker.Apply(List.<JCExpression>nil(), getter, List.of(access.selected, + cx.treeMaker.Literal(access.name.toString()))); + } + public JCExpression fetchSetterStatement(JCAssign assign) { JCFieldAccess setter = cx.treeMaker.Select(nameToExpression(ConstPool.TESTABLE_PRIVATE_ACCESSOR), cx.names.fromString("set")); diff --git a/testable-processor/src/main/java/com/alibaba/testable/processor/translator/EnablePrivateAccessTranslator.java b/testable-processor/src/main/java/com/alibaba/testable/processor/translator/EnablePrivateAccessTranslator.java index 3ae3c1b..14864fa 100644 --- a/testable-processor/src/main/java/com/alibaba/testable/processor/translator/EnablePrivateAccessTranslator.java +++ b/testable-processor/src/main/java/com/alibaba/testable/processor/translator/EnablePrivateAccessTranslator.java @@ -18,10 +18,23 @@ import java.lang.reflect.Modifier; */ public class EnablePrivateAccessTranslator extends BaseTranslator { + /** + * Name of source class + */ private final String sourceClassName; + /** + * Fields of source class instance in the test class + */ private final ListBuffer<Name> sourceClassIns = new ListBuffer<Name>(); + /** + * Record private and final fields + */ private final ListBuffer<String> privateOrFinalFields = new ListBuffer<String>(); + /** + * Record private methods + */ private final ListBuffer<String> privateMethods = new ListBuffer<String>(); + private final PrivateAccessStatementGenerator privateAccessStatementGenerator; public EnablePrivateAccessTranslator(String pkgName, String testClassName, TestableContext cx) { @@ -61,20 +74,24 @@ public class EnablePrivateAccessTranslator extends BaseTranslator { */ @Override public void visitExec(JCExpressionStatement jcExpressionStatement) { + // visitExec could be an assign statement to a private field if (jcExpressionStatement.expr.getClass().equals(JCAssign.class) && isPrivateField((JCAssign)jcExpressionStatement.expr)) { jcExpressionStatement.expr = privateAccessStatementGenerator.fetchSetterStatement( (JCAssign)jcExpressionStatement.expr); } + // visitExec could be an invoke jcExpressionStatement.expr = checkAndExchange(jcExpressionStatement.expr); super.visitExec(jcExpressionStatement); } /** * For private invoke invocation break point + * call(d.privateMethod(args)) -> call(PrivateAccessor.invoke(d, "privateMethod", args)) */ @Override public void visitApply(JCMethodInvocation tree) { + // parameter of invocation could be an invoke or field access tree.args = checkAndExchange(tree.args); super.visitApply(tree); } @@ -97,6 +114,12 @@ public class EnablePrivateAccessTranslator extends BaseTranslator { @Override protected JCExpression checkAndExchange(JCExpression expr) { + // check is accessing a private field of source class + if (expr.getClass().equals(JCFieldAccess.class) && + isPrivateField((JCFieldAccess)expr)) { + expr = privateAccessStatementGenerator.fetchGetterStatement((JCFieldAccess)expr); + } + // check is invoking a private method of source class if (expr.getClass().equals(JCMethodInvocation.class) && isPrivateMethod((JCMethodInvocation)expr)) { expr = privateAccessStatementGenerator.fetchInvokeStatement((JCMethodInvocation)expr); @@ -104,11 +127,17 @@ public class EnablePrivateAccessTranslator extends BaseTranslator { return expr; } - private boolean isPrivateField(JCAssign expr) { - return expr.lhs.getClass().equals(JCFieldAccess.class) && - ((JCFieldAccess)(expr).lhs).selected.getClass().equals(JCIdent.class) && - sourceClassIns.contains(((JCIdent)((JCFieldAccess)(expr).lhs).selected).name) && - privateOrFinalFields.contains(((JCFieldAccess)(expr).lhs).name.toString()); + private boolean isPrivateField(JCFieldAccess access) { + return access.selected.getClass().equals(JCIdent.class) && + sourceClassIns.contains(((JCIdent)access.selected).name) && + privateOrFinalFields.contains(access.name.toString()); + } + + private boolean isPrivateField(JCAssign assign) { + return assign.lhs.getClass().equals(JCFieldAccess.class) && + ((JCFieldAccess)(assign).lhs).selected.getClass().equals(JCIdent.class) && + sourceClassIns.contains(((JCIdent)((JCFieldAccess)(assign).lhs).selected).name) && + privateOrFinalFields.contains(((JCFieldAccess)(assign).lhs).name.toString()); } private boolean isPrivateMethod(JCMethodInvocation expr) {