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

TestNG+Mockito批量执行测试出现Assertion null异常求助

问题分析与解决方案

你遇到的问题核心是测试方法之间共享Mock对象状态,导致后续测试的Stubbing被前序测试干扰,加上部分测试里错误的reset()调用,最终导致loginBean的方法返回null,触发TestNG断言的AssertionError: null。

具体原因拆解

  • Mock初始化时机错误:你用@BeforeClass初始化Mock对象,这意味着所有测试方法共享同一组Mock实例。前一个测试设置的Stubbing(比如when(commonDAO...).thenReturn(...))会保留到下一个测试,导致预期行为混乱。
  • 不必要的reset()调用:比如在negTestCheckIsReportCreatedByCustomerReportFlagByNo里,你刚给commonDAO设置了Stubbing,立刻调用reset(commonDAO),这会清空所有Stubbing,导致loginBean.checkIsReportCreatedByCustomer调用commonDAO.getLoginControllerValue时返回null,进而loginBean的方法可能返回null,触发断言异常。
  • 测试用例重复且依赖状态:多个测试用例逻辑重复(比如negTestCheckLoginAlertReportFlagByNo和negTestCheckIsReportCreatedByCustomerReportFlagByNo逻辑几乎一致),而且没有在测试前重置状态,导致状态污染。

修复步骤

  1. 将Mock初始化移到@BeforeMethod:让每个测试方法执行前都重新初始化Mock,确保每个测试都是独立的干净状态。
  2. 移除错误的reset()调用:不要在设置Stubbing后立刻重置Mock,除非你明确需要清空状态。
  3. 确保Stubbing覆盖所有必要场景:保证loginBean依赖的所有方法都有正确的Stubbing,避免返回null。
  4. 优化重复测试用例:可以用TestNG的@DataProvider来参数化测试,减少重复代码。

修改后的代码示例

public class TestLoginController { 
    @Mock DBResourseLoader dbResourceLoader; 
    @Mock MessageLoader messageLoader; 
    @Mock Logger logger; 
    @Mock Properties properties; 
    @Mock Properties databaseProperties; 
    @Mock DBConnector connector; 
    @Mock Connection connection; 
    @Mock PropertyConfigurator propertyConfigurator; 
    @InjectMocks LoginBean loginBean; 
    @Mock CommonDAO commonDAO; 
    @Mock PreparedStatement preparedStatement; 
    @Mock ResultSet resultSet; 

    // 改为@BeforeMethod,每个测试前初始化Mock,确保状态独立
    @BeforeMethod 
    public void setup() { 
        try { 
            MockitoAnnotations.initMocks(this); 
            when(messageLoader.getMessageStatement("")).thenReturn(""); 
            when(dbResourceLoader.getJdbcDatasource()).thenReturn(properties); 
            when(dbResourceLoader.getLogger()).thenReturn(databaseProperties); 
            when(dbResourceLoader.getLogger(Logger.class)).thenReturn(logger); 
            when(connector.getConnection(true)).thenReturn(connection); 
        }catch(Exception e) { 
            e.printStackTrace(); 
        } 
    } 

    @Test 
    public void negTestCheckIsReportCreatedByCustomerReportFlagByNo() { 
        try { 
            when(commonDAO.getLoginControllerValue("LOGIN_CONTROLLER")).thenReturn("NO"); 
            boolean result = loginBean.checkIsReportCreatedByCustomer("1234@1234.com");
            // 先断言结果非空,避免null引发的断言异常
            assertNotNull(result, "checkIsReportCreatedByCustomer返回了null");
            assertNotEquals(false, result); 
        }catch(Exception e) { 
            e.printStackTrace(); 
        } 
    } 

    @Test 
    public void posTestCheckIsReportCreatedByCustomerReportFlagByNo() { 
        try { 
            when(commonDAO.getLoginControllerValue("LOGIN_CONTROLLER")).thenReturn("NO"); 
            boolean result = loginBean.checkIsReportCreatedByCustomer("1234@1234.com");
            assertNotNull(result, "checkIsReportCreatedByCustomer返回了null");
            assertEquals(true, result); 
        }catch(Exception e) { 
            e.printStackTrace(); 
        } 
    } 

    @Test 
    public void negTestCheckIsReportCreatedByCustomerByReportFlagYes() { 
        try { 
            when(commonDAO.getLoginControllerValue("LOGIN_CONTROLLER")).thenReturn("YES"); 
            when(commonDAO.getCustomerReportCreationValue("1234@gmail.com")).thenReturn(false); 
            boolean result = loginBean.checkIsReportCreatedByCustomer("1234@1234.com");
            assertNotNull(result, "checkIsReportCreatedByCustomer返回了null");
            assertNotEquals(true, result); 
        }catch(Exception e) { 
            e.printStackTrace(); 
        } 
    } 

    // 其余测试用例按同样方式修改,确保每个测试的Stubbing独立且结果非空
}

额外优化建议

使用TestNG的@DataProvider参数化测试,大幅减少重复代码:

@DataProvider(name = "isReportCreatedData")
public Object[][] isReportCreatedData() {
    return new Object[][] {
        // 场景:报告标志NO,预期true,否定测试(断言不是false)
        {"NO", null, true, false, "negTestReportFlagNo"},
        // 场景:报告标志NO,预期true,肯定测试
        {"NO", null, true, true, "posTestReportFlagNo"},
        // 场景:报告标志YES、客户未创建报告,预期false,否定测试(断言不是true)
        {"YES", false, false, true, "negTestReportFlagYesNoCreation"},
        // 场景:报告标志YES、客户已创建报告,预期true,肯定测试
        {"YES", true, true, true, "posTestReportFlagYesCreation"}
    };
}

@Test(dataProvider = "isReportCreatedData")
public void testCheckIsReportCreatedByCustomer(String controllerFlag, Boolean customerCreationValue, boolean expectedResult, boolean isPositiveTest, String testName) {
    when(commonDAO.getLoginControllerValue("LOGIN_CONTROLLER")).thenReturn(controllerFlag);
    if (customerCreationValue != null) {
        when(commonDAO.getCustomerReportCreationValue("1234@1234.com")).thenReturn(customerCreationValue);
    }
    boolean result = loginBean.checkIsReportCreatedByCustomer("1234@1234.com");
    assertNotNull(result, testName + ": 方法返回null");
    if (isPositiveTest) {
        assertEquals(result, expectedResult, testName + ": 断言失败");
    } else {
        assertNotEquals(result, expectedResult, testName + ": 断言失败");
    }
}

这样既简化了代码,又让测试逻辑更清晰,后续维护也更方便。

内容的提问来源于stack exchange,提问作者Muthu vignesh k

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 09:26:35