【问题标题】:Junit tests fail due shared instances (i think)由于共享实例,Junit 测试失败(我认为)
【发布时间】:2020-05-05 06:40:23
【问题描述】:

我认为我的问题与这个问题有关: Tests pass when run individually but not when the whole test class run

我的测试都单独通过,但是当我运行测试类时,其中两个失败了。调试表明 assertEquals 期望的状态与代码中给出的不同,主要是来自不同测试的状态。解决方案应该使用 @Before 或 @After 并进行清理,但我在使用时遇到了麻烦,因为实例是从 Spring 自动装配的。

主要问题:我的想法是否正确以及如何“重置”自动装配的实例?

@SpringBootTest
class StatementProcessorServiceTest {

    @Autowired
    StatementProcessorService statementProcessorService;

    private StatementProcessorInputModel setup(long id, boolean endbalanceCorrect) {
        StatementProcessorInputModel statementProcessorInputModel = new StatementProcessorInputModel();
        statementProcessorInputModel.startBalance = 1.00;
        statementProcessorInputModel.id= id;
        statementProcessorInputModel.startBalance = 1.00;
        statementProcessorInputModel.mutation = 1.00;
        statementProcessorInputModel.endBalance = endbalanceCorrect ? 2.00 : 1.00;
        return statementProcessorInputModel;
    }

    @Test
    void verify_with_all_good_settings()
    {
        StatementProcessorResponseModel sut;
        sut = statementProcessorService.validate(setup(1, true));
        Assert.assertEquals(sut.result, "SUCCESSFUL");
    }

    @Test
    void verify_with_good_settings_but_duplicated_reference()
    {
        StatementProcessorResponseModel sutFirst = statementProcessorService.validate(setup(2, true));
        Assert.assertEquals(sutFirst.result, "SUCCESSFUL");
        StatementProcessorResponseModel sutSecond;
        sutSecond = statementProcessorService.validate(setup(2, true));
        Assert.assertEquals(sutSecond.result, "DUPLICATE_REFERENCE");
    }

    @Test
    void verify_with_wrong_endBalance_and_good_reference()
    {
        StatementProcessorResponseModel sut = statementProcessorService.validate(setup(3, false));
        Assert.assertEquals(sut.result, "INCORRECT_END_BALANCE");
    }

    @Test
    void verify_with_all_wrong_settings()
    {
        StatementProcessorResponseModel sutFirst = statementProcessorService.validate(setup(4, true));
        Assert.assertEquals(sutFirst.result, "SUCCESSFUL");
        StatementProcessorResponseModel sutSecond = statementProcessorService.validate(setup(4, false));
        Assert.assertEquals(sutSecond.result, "DUPLICATE_REFERENCE_INCORRECT_END_BALANCE");
    }
}

编辑 1:添加服务代码

@Component
public class StatementProcessorService {
    private StatementProcessorResponseModel responseModel = new StatementProcessorResponseModel();
    private static ArrayList<Long> usedReferences = new ArrayList<>();

    public StatementProcessorResponseModel validate(StatementProcessorInputModel inputModel){
        if(!validateUniqueReference(inputModel.transactionReference))
        {
            responseModel.errorRecords.add("account_number_of_ inCorrectEndBalance _record");
            responseModel.result = "DUPLICATE_REFERENCE";
        }

        if(!validateMutation(inputModel))
        {
            responseModel.errorRecords.add("account_number_of_ inCorrectEndBalance _record");
            if (!responseModel.result.isBlank()){
                responseModel.result = responseModel.result + "_INCORRECT_END_BALANCE";
            }
            else{
                responseModel.result = "INCORRECT_END_BALANCE";
            }
        }

        if (responseModel.result.isBlank()){
            responseModel.result = "SUCCESSFUL";
        }
        return responseModel;
    }

【问题讨论】:

    标签: java spring junit spring-boot-test


    【解决方案1】:

    如果您使用@Transactional 标记修改数据库的测试用例/类,那么测试运行程序将在测试后回滚事务。

    您可以参考reference documentation regarding test transactions。

    【讨论】:

    • 对于这个项目(这是一个小爱好项目)我不使用数据库或 JPA。
    • 可能 StatementProcessorService 正在存储状态 somewhere。此状态是您需要重置的状态。如果您将状态存储在数据库中,@Transactional 将起作用,您如何为场景重置状态将取决于您如何存储它。
    • 我添加了我的原始服务代码,你是对的。在我将 ResponseModel 的启动移动到方法而不是类之后,它会重置每个测试并且它们通过。感谢您的时间和帮助!
    • 不确定我是否理解,但很高兴我能提供帮助 :-)
    【解决方案2】:

    您似乎没有使用数据库来存储状态,因此在不知道如何存储此状态的情况下很难添加完整的答案。不管StatementProcessorService 必须以某种方式存储状态,所以我将尝试说明如何在测试之间重置此状态,也许您可​​以根据自己的情况进行调整。

    从一个基本的“它存储在内存中”示例开始

    class StatementProcessorService {
      // assuming you're storing state in memory for now
      // here we're just creating a shared map, which is where we presume you're putting data
      private Map<String, String> state = new HashMap<>();
    }
    

    这样做的最简单的方法是公开一个从StatementProcessorService外部重置状态的方法。

    class StatementProcessorService {
      // ...
    
      /** Reset state */
      @VisibleForTesting
      public void reset() { state.clear(); }
    }
    

    您还可以使用注入状态保持器,在测试期间它本身可以公开重置方法

    class StateHolder {
      // accessor/mutator methods
    }
    
    class TestStateHolder extends StateHolder {
      // ...
    
      public void reset() { ... }
    }
    
    class StatementProcessorService {
      @Autowired
      private StateHolder state;
    }
    

    最后,您可以使用 Mockito 模拟 StateHolder

    @SpringBootTest
    class StatementProcessorServiceTest {
      @MockBean StateHolder state;
    }
    

    与 Spring 一样,有很多方法可以实现您的目标。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-03-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多