You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java单元测试中ElementNSImpl无法转换为Delivery类的ClassCastException问题解决及测试方案

解决PrintnetRequestTranslator单元测试中的ClassCastException问题

问题背景

我正在为PrintnetRequestTranslator类编写单元测试,该类继承自RequestTranslator<MutationOverviewDocRequest, RequestContent>。执行测试时,尝试将result.getDelivery()强制转换为Delivery类时抛出了ClassCastException,异常信息如下:

java.lang.ClassCastException: class com.sun.org.apache.xerces.internal.dom.ElementNSImpl cannot be cast to class printnet.generated.Delivery (com.sun.org.apache.xerces.internal.dom.ElementNSImpl is in module java.xml of loader 'bootstrap'; ......printnet.generated.Delivery is in unnamed module of loader 'app')

相关核心代码片段如下:

PrintnetRequestTranslator关键逻辑

@Override
public RequestContent translateRequest() {
    // ... 初始化Delivery及内部属性的业务逻辑 ...
    requestContent.setDelivery(getDocument(delivery)); // 这里是核心:将Delivery对象序列化为DOM Element
    return requestContent;
}

private Element getDocument(final Object any) {
    try {
        // JAXB序列化逻辑:把传入的对象转成org.w3c.dom.Element
        final Marshaller marshaller = JAXB_CONTEXT.createMarshaller();
        marshaller.marshal(any, document);
        return document.getDocumentElement();
    } catch (final ParserConfigurationException | JAXBException ex) {
        throw new CardLimitsServiceException(ex);
    }
}

RequestContent类定义

@XmlRootElement(name = "RequestContent")
public class RequestContent {
    @XmlElement(name = "Delivery", required = true)
    protected Object delivery;
    // ... getter/setter方法 ...
}

当前单元测试代码

class PrintnetRequestTranslatorTest { 
    private PrintnetRequestTranslator classUnderTest; 
    @BeforeEach 
    void setup() { 
        classUnderTest = new PrintnetRequestTranslator(TestData.mockMutationDocumentRequest()); 
    } 
    @Test 
    @DisplayName("Printnet Translator Success Scenario") 
    void testPrintnetRequestSuccess() { 
        final var result = classUnderTest.translateRequest(); 
        final Delivery delivery = (Delivery) result.getDelivery(); // 抛出类型转换异常
        assertAll("Printnet Request Translator Test : ", () -> assertEquals(TestUtils.PRINTNET_OUTPUT_FORMAT, result.getOutputFormat().toString())); 
    } 
}

问题根源

从代码逻辑可以看到:translateRequest方法最后调用getDocument(delivery),这个方法会把内存中的Delivery对象通过JAXB序列化成org.w3c.dom.Element实例,再赋值给RequestContent的delivery字段。所以测试中拿到的result.getDelivery()实际是一个DOM Element对象,而非原始的Delivery对象,强制转换自然会失败。

解决方案

根据你的测试目标,有两种针对性的解决思路:

方案1:验证序列化后的XML内容(适合集成测试场景)

如果你的测试需要确保JAXB序列化后的XML结构符合预期,可以直接操作DOM Element来断言内容:

@Test 
@DisplayName("Printnet Translator Success Scenario - Verify XML Output") 
void testPrintnetRequestSuccess() { 
    final var result = classUnderTest.translateRequest(); 
    
    // 验证OutputFormat配置
    assertEquals(TestUtils.PRINTNET_OUTPUT_FORMAT, result.getOutputFormat().toString());
    
    // 转换为Element并验证XML节点内容
    Element deliveryElement = (Element) result.getDelivery();
    
    // 示例:验证文档名称
    NodeList documentNodes = deliveryElement.getElementsByTagName("Document");
    assertEquals(1, documentNodes.getLength());
    Element docElement = (Element) documentNodes.item(0);
    assertEquals("Mutatieoverzicht", docElement.getElementsByTagName("Name").item(0).getTextContent());
    
    // 可继续添加更多断言:比如客户ID、地址、产品额度变更等XML节点内容
}

方案2:Mock序列化方法,聚焦业务逻辑(适合纯单元测试)

如果你的测试目标是验证translateRequest中的业务逻辑(比如Delivery对象的属性组装是否正确),而非JAXB序列化的正确性,可以用Mockito的Spy来MockgetDocument方法,让它直接返回原始的Delivery对象:

首先确保引入Mockito依赖,然后修改测试代码:

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Spy;
import org.mockito.MockitoAnnotations;
import static org.mockito.Mockito.when;

class PrintnetRequestTranslatorTest { 
    @Spy // 使用Spy实现部分Mock
    private PrintnetRequestTranslator classUnderTest; 

    @BeforeEach 
    void setup() { 
        MockitoAnnotations.openMocks(this);
        classUnderTest = new PrintnetRequestTranslator(TestData.mockMutationDocumentRequest());
    } 

    @Test 
    @DisplayName("Printnet Translator Success Scenario - Verify Business Logic") 
    void testPrintnetRequestSuccess() throws Exception { 
        // Mock getDocument方法,直接返回传入的原始对象
        when(classUnderTest.getDocument(org.mockito.ArgumentMatchers.any())).thenAnswer(invocation -> {
            return invocation.getArgument(0);
        });
        
        final var result = classUnderTest.translateRequest(); 
        final Delivery delivery = (Delivery) result.getDelivery(); // 现在可以正常转换
        
        // 断言Delivery的业务属性
        assertNotNull(delivery.getCreationDateTime());
        assertEquals(1, delivery.getDocuments().size());
        Delivery.Document document = delivery.getDocuments().get(0);
        assertEquals("Mutatieoverzicht", document.getName());
        
        // 继续断言客户信息、产品额度变更等业务逻辑
        Delivery.Document.GenericData.Clients.Client client = document.getGenericData().getClients().getClients().get(0);
        assertEquals(TestData.EXPECTED_CUSTOMER_ID, client.getId());
        assertEquals(TestData.EXPECTED_INITIALS, client.getInitials());
        
        // 验证OutputFormat
        assertEquals(TestUtils.PRINTNET_OUTPUT_FORMAT, result.getOutputFormat().toString());
    } 
}

额外优化建议

  1. 隔离外部依赖:单元测试应聚焦业务逻辑,JAXB序列化属于外部依赖,建议用Mock隔离,避免测试受序列化逻辑影响。
  2. 拆分测试关注点:可以将测试拆分为两个独立用例:一个验证Delivery对象组装逻辑,另一个单独验证JAXB序列化的正确性(作为集成测试)。
  3. 优化类型安全性:RequestContent的delivery字段用Object类型会导致类型不安全,建议修改为Element或泛型,从代码层面避免类型转换错误。

内容的提问来源于stack exchange,提问作者Sujan Ponnappa

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.28 22:52:29