This is an automated email from the ASF dual-hosted git repository. jianbin pushed a commit to branch 2.x in repository https://gitbox.apache.org/repos/asf/incubator-seata.git
The following commit(s) were added to refs/heads/2.x by this push: new 13e42810af test: add UT for TccAnnotationProcessor class (#7398) 13e42810af is described below commit 13e42810af181f6a5d2a911870345930b166ab88 Author: OmCheeLin <19563671...@163.com> AuthorDate: Fri Jun 6 14:33:15 2025 +0800 test: add UT for TccAnnotationProcessor class (#7398) --- changes/en-us/2.x.md | 1 + changes/zh-cn/2.x.md | 1 + spring/pom.xml | 15 +- .../spring/tcc/TccAnnotationProcessorTest.java | 177 +++++++++++++++++++++ 4 files changed, 193 insertions(+), 1 deletion(-) diff --git a/changes/en-us/2.x.md b/changes/en-us/2.x.md index 119c6d55b8..8e62ab2a33 100644 --- a/changes/en-us/2.x.md +++ b/changes/en-us/2.x.md @@ -79,6 +79,7 @@ Add changes here for all PR submitted to the 2.x branch. - [[#7295](https://github.com/apache/incubator-seata/pull/7295)] updated 3 tests in StringUtilsTest to use parameterized unit testing - [[#7205](https://github.com/apache/incubator-seata/issues/7205)] add UT for namingserver module - [[#7359](https://github.com/apache/incubator-seata/issues/7359)] merge submodule test reports +- [[#7379](https://github.com/apache/incubator-seata/issues/7379)] add UT for TccAnnotationProcessor class ### refactor: diff --git a/changes/zh-cn/2.x.md b/changes/zh-cn/2.x.md index 104d530b29..cc7409fb4e 100644 --- a/changes/zh-cn/2.x.md +++ b/changes/zh-cn/2.x.md @@ -80,6 +80,7 @@ - [[#7295](https://github.com/apache/incubator-seata/pull/7295)] 重构了 StringUtilsTest 中的 3 个测试,改为使用参数化单元测试 - [[#7205](https://github.com/apache/incubator-seata/issues/7205)] 为 namingserver module 添加单元测试 - [[#7359](https://github.com/apache/incubator-seata/issues/7359)] 合并所有模块的单测报告,准确显示单测覆盖度 +- [[#7379](https://github.com/apache/incubator-seata/issues/7379)] 为 TccAnnotationProcessor 添加了单元测试 ### refactor: diff --git a/spring/pom.xml b/spring/pom.xml index 9121af2b6f..f6613638ad 100644 --- a/spring/pom.xml +++ b/spring/pom.xml @@ -81,6 +81,19 @@ <artifactId>kotlin-test</artifactId> <scope>test</scope> </dependency> + <dependency> + <groupId>ch.qos.logback</groupId> + <artifactId>logback-classic</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + </dependency> + <dependency> + <groupId>ch.qos.logback</groupId> + <artifactId>logback-core</artifactId> + </dependency> <dependency> <groupId>org.jetbrains.kotlinx</groupId> @@ -114,4 +127,4 @@ </build> -</project> \ No newline at end of file +</project> diff --git a/spring/src/test/java/org/apache/seata/spring/tcc/TccAnnotationProcessorTest.java b/spring/src/test/java/org/apache/seata/spring/tcc/TccAnnotationProcessorTest.java new file mode 100644 index 0000000000..467b52edc7 --- /dev/null +++ b/spring/src/test/java/org/apache/seata/spring/tcc/TccAnnotationProcessorTest.java @@ -0,0 +1,177 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.seata.spring.tcc; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.read.ListAppender; +import org.apache.seata.integration.tx.api.util.ProxyUtil; +import org.apache.seata.rm.tcc.api.TwoPhaseBusinessAction; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.slf4j.LoggerFactory; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.List; +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class TccAnnotationProcessorTest { + private ListAppender<ILoggingEvent> listAppender; + private TccAnnotationProcessor processor; + private Set<String> proxied; + private List<Class<? extends Annotation>> annotations; + + @BeforeEach + public void setUp() throws NoSuchFieldException, IllegalAccessException { + processor = new TccAnnotationProcessor(); + + Logger logger = (Logger) LoggerFactory.getLogger(TccAnnotationProcessor.class); + listAppender = new ListAppender<>(); + listAppender.start(); + logger.addAppender(listAppender); + + Field proxiedField = TccAnnotationProcessor.class.getDeclaredField("PROXIED_SET"); + proxiedField.setAccessible(true); + proxied = (Set<String>) proxiedField.get(null); + + Field annotationsField = TccAnnotationProcessor.class.getDeclaredField("ANNOTATIONS"); + annotationsField.setAccessible(true); + annotations = (List<Class<? extends Annotation>>) annotationsField.get(null); + } + + @AfterEach + public void tearDown() { + listAppender.stop(); + listAppender.list.clear(); + proxied.clear(); + annotations.clear(); + } + + @interface MockReference { + } + + static class MockTccService { + @TwoPhaseBusinessAction(name = "testAction") + public void tryMethod() { + } + } + + static class TestBean { + @MockReference + public MockTccService tccService = new MockTccService(); + + @MockReference + public MockTccService nullService = null; + } + + @Test + public void testAddTccAdviseCreatesProxy() throws Exception { + TestBean bean = new TestBean(); + Field field = TestBean.class.getField("tccService"); + + Object originalValue = field.get(bean); + + try (MockedStatic<ProxyUtil> mockedStatic = Mockito.mockStatic(ProxyUtil.class)) { + mockedStatic.when(() -> ProxyUtil.createProxy(bean, "testBean")).thenAnswer(invocation -> { + Object arg = invocation.getArgument(0); + if (arg instanceof TestBean) { + return Mockito.spy(new MockTccService()); + } + return arg; + }); + + processor.addTccAdvise(bean, "testBean", field, MockTccService.class); + } + + Object newValue = field.get(bean); + assertNotEquals(originalValue, newValue, "Proxy should replace original field"); + String expectedLog = String.format("Bean[%s] with name [%s] would use proxy", bean.getClass().getName(), "tccService"); + boolean containsLog = listAppender.list.stream() + .anyMatch(event -> event.getFormattedMessage().equals(expectedLog)); + assertTrue(containsLog, "Logs should contain exact proxy injection info: " + expectedLog); + } + + @Test + public void testAddTccAdviseFieldValueNull() throws Exception { + TestBean bean = new TestBean(); + Field nullField = TestBean.class.getField("nullService"); + processor.addTccAdvise(bean, "testBean", nullField, MockTccService.class); + assertNull(nullField.get(bean)); + boolean noLogsPrinted = listAppender.list.isEmpty(); + assertTrue(noLogsPrinted, "No logs should be printed when field value is null"); + } + + @Test + public void testProcessWithNullAnnotation() { + processor.process(new TestBean(), "testBean", null); + assertTrue(proxied.isEmpty(), "Should not proxy if annotation is null"); + } + + @Test + public void testProcessWhenAlreadyProxied() { + proxied.add("testBean"); + processor.process(new TestBean(), "testBean", MockReference.class); + assertTrue(proxied.contains("testBean")); + } + + @Test + public void testProcessFieldWithAnnotation() { + annotations.add(MockReference.class); + TestBean bean = new TestBean(); + + try (MockedStatic<ProxyUtil> mockedStatic = Mockito.mockStatic(ProxyUtil.class)) { + mockedStatic.when(() -> ProxyUtil.createProxy(bean, "testBean")).thenReturn(Mockito.spy(new MockTccService())); + + processor.postProcessBeforeInitialization(bean, "testBean"); + } + + assertTrue(proxied.contains("testBean")); + } + + @Test + public void testLoadAnnotationWithReflection() throws Exception { + Method loadAnnotationMethod = TccAnnotationProcessor.class.getDeclaredMethod("loadAnnotation", String.class); + loadAnnotationMethod.setAccessible(true); + + Class<?> annotationClass = (Class<?>) loadAnnotationMethod.invoke(null, "java.lang.Override"); + assertNotNull(annotationClass); + + Object nullClass = loadAnnotationMethod.invoke(null, "non.existing.AnnotationClass"); + assertNull(nullClass); + } + + + @Test + public void testPostProcessAfterInitializationReturnsBean() { + TestBean bean = new TestBean(); + Object result = processor.postProcessAfterInitialization(bean, "testBean"); + assertSame(bean, result); + } +} --------------------------------------------------------------------- To unsubscribe, e-mail: notifications-unsubscr...@seata.apache.org For additional commands, e-mail: notifications-h...@seata.apache.org