JUnit测试覆盖率仅50%:JSONObject相关代码块未覆盖求助
AEM资产上传服务测试覆盖率问题排查
问题背景
实现了AEM资产上传的Java服务类AssetUploadAPIServiceImpl,对应的JUnit测试用例执行后代码覆盖率仅为50%,JSONObject解析相关代码块未被覆盖,调整测试类后覆盖率仍未提升。
实现类代码(AssetUploadAPIServiceImpl)
import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.util.Base64; import java.util.HashMap; import java.util.Map; import org.apache.sling.api.SlingHttpServletRequest; import org.apache.sling.api.resource.LoginException; import org.apache.sling.api.resource.ResourceResolver; import org.apache.sling.api.resource.ResourceResolverFactory; import org.json.JSONObject; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Reference; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.day.cq.dam.api.Asset; import com.day.cq.dam.api.AssetManager; @Component(service = AssetUploadAPIService.class) public class AssetUploadAPIServiceImpl implements AssetUploadAPIService { @Reference transient ResourceResolverFactory resolverFactory; private transient Map<String, Object> param = new HashMap<>(); private static final Logger LOGGER = LoggerFactory.getLogger(AssetUploadAPIServiceImpl.class); public AssetUploadAPIServiceImpl() { param.put(ResourceResolverFactory.SUBSERVICE, "datawrite"); } private ResourceResolver getResourceResolver() throws LoginException { return resolverFactory.getServiceResourceResolver(param); } @Override public ServletResponse<String> getAssetUploadResponse(SlingHttpServletRequest request) { try (ResourceResolver resourceResolver = getResourceResolver()) { StringBuilder sb = new StringBuilder(); BufferedReader reader = request.getReader(); try { String line = null; while ((line = reader.readLine()) != null) { sb.append(line); } } finally { reader.close(); } LOGGER.info("POST JSON data: {}", sb); JSONObject jsonObject = new JSONObject(sb.toString()); String assetPath = jsonObject.getString("assetPath"); String mimeType = jsonObject.getString("mimeType"); String encoding = jsonObject.getString("encoding"); String asset = jsonObject.getString("asset"); String imageType = mimeType.split("/")[1]; LOGGER.info("assetPath: {}, mimeType: {}, encoding: {}, asset: {}, imageType: {}", assetPath, mimeType,encoding, asset, imageType); InputStream targetStream = new ByteArrayInputStream(Base64.getDecoder().decode(asset.getBytes(StandardCharsets.UTF_8))); AssetManager assetManager = resourceResolver.adaptTo(AssetManager.class); Asset imageAsset = assetManager.createAsset((assetPath + "." + imageType), targetStream, mimeType, true); return new ServletResponse<>(ServletResponse.ServletResponseType.RES_200, imageAsset.getPath()); } catch (Exception e) { LOGGER.error(String.format("There was an Error === " + e)); return new ServletResponse<>(ServletResponse.ServletResponseType.RES_400, e.getMessage()); } } }
测试类代码(AssetUploadAPIServiceImplTest)
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.util.HashMap; import javax.jcr.Session; import org.apache.sling.api.SlingHttpServletRequest; import org.apache.sling.api.resource.LoginException; import org.apache.sling.api.resource.ResourceResolver; import org.apache.sling.api.resource.ResourceResolverFactory; import org.apache.sling.api.wrappers.SlingHttpServletRequestWrapper; import org.apache.sling.testing.mock.sling.ResourceResolverType; import org.json.JSONException; import org.junit.Rule; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import com.day.cq.wcm.foundation.forms.FormsHandlingRequest; import io.wcm.testing.mock.aem.junit5.AemContext; class AssetUploadAPIServiceImplTest { @Mock private ResourceResolverFactory resolverFactory; @Mock private Session session; @Mock private ResourceResolver resourceResolver; @Rule private final AemContext context = new AemContext(ResourceResolverType.JCR_MOCK); @InjectMocks private AssetUploadAPIServiceImpl assetUploadAPIService = new AssetUploadAPIServiceImpl(); @BeforeEach public void setup() { MockitoAnnotations.initMocks(this); HashMap<String, Object> parameterMap = new HashMap<>(); parameterMap.put("assetPath", "/content/dam/public/assets"); parameterMap.put("mimeType", "image/png"); parameterMap.put("encoding", "base64"); context.request().setParameterMap(parameterMap); } @Test void getAssetUploadResponse() throws LoginException, JSONException { when(resolverFactory.getServiceResourceResolver(any())).thenReturn(resourceResolver); when(resourceResolver.adaptTo(Session.class)).thenReturn(session); ServletResponse<String> actualAssetUploadResponse = assetUploadAPIService.getAssetUploadResponse(new FormsHandlingRequest(new SlingHttpServletRequestWrapper(mock(SlingHttpServletRequest.class)))); assertNull(actualAssetUploadResponse.getPayload()); assertEquals(ServletResponse.ServletResponseType.RES_400, actualAssetUploadResponse.getStatus()); } }
未覆盖代码块
JSONObject jsonObject = new JSONObject(sb.toString()); String assetPath = jsonObject.getString("assetPath"); String mimeType = jsonObject.getString("mimeType"); String encoding = jsonObject.getString("encoding"); String asset = jsonObject.getString("asset"); String imageType = mimeType.split("/")[1];
排查建议
- 请求体模拟错误:实现类通过
request.getReader()读取JSON请求体,但当前测试仅设置了请求参数,未MockgetReader()返回有效JSON内容,导致代码直接进入异常分支。需MockgetReader()返回包含完整测试JSON的BufferedReader。 - 请求包装类冗余:测试中嵌套使用
FormsHandlingRequest和SlingHttpServletRequestWrapper,可能导致Mock的getReader()失效。直接MockSlingHttpServletRequest并设置方法返回值更可靠。 - 缺少正常流程测试:当前测试仅覆盖了异常分支(返回400),未编写正常流程的测试用例。需新增测试方法,模拟完整JSON请求体、正确的
ResourceResolver和AssetManager行为,验证200分支的执行。 - Mock逻辑不完整:未Mock
AssetManager的createAsset()方法,即使请求体正确,后续也会抛出异常进入400分支。需MockresourceResolver.adaptTo(AssetManager.class)返回Mock实例,并设置createAsset()返回Mock的Asset。
具体调整示例
修改测试方法,正确Mock请求体与依赖:
@Test void getAssetUploadResponse_Success() throws Exception { // 构造测试用JSON字符串 String testJson = "{\"assetPath\": \"/content/dam/public/assets\", \"mimeType\": \"image/png\", \"encoding\": \"base64\", \"asset\": \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==\"}"; BufferedReader mockReader = new BufferedReader(new StringReader(testJson)); // Mock请求的getReader方法 SlingHttpServletRequest mockRequest = mock(SlingHttpServletRequest.class); when(mockRequest.getReader()).thenReturn(mockReader); // Mock ResourceResolver和AssetManager AssetManager mockAssetManager = mock(AssetManager.class); Asset mockAsset = mock(Asset.class); when(resolverFactory.getServiceResourceResolver(any())).thenReturn(resourceResolver); when(resourceResolver.adaptTo(AssetManager.class)).thenReturn(mockAssetManager); when(mockAssetManager.createAsset(anyString(), any(InputStream.class), anyString(), anyBoolean())).thenReturn(mockAsset); when(mockAsset.getPath()).thenReturn("/content/dam/public/assets.png"); // 执行测试 ServletResponse<String> response = assetUploadAPIService.getAssetUploadResponse(mockRequest); // 验证结果 assertEquals(ServletResponse.ServletResponseType.RES_200, response.getStatus()); assertEquals("/content/dam/public/assets.png", response.getPayload()); }
内容的提问来源于stack exchange,提问作者snaik
相关产品推荐
相关产品推荐

