【问题标题】:Spring Boot unit testing a Service, manual tests pass but mocked service failsSpring Boot对服务进行单元测试,手动测试通过但模拟服务失败
【发布时间】:2016-11-17 16:19:11
【问题描述】:

为了测试我的服务逻辑,我必须在 3 个存储库中设置一些初始测试数据。测试的那部分工作正常,但是服务应该进行的更新永远不会在存储库中发生,并且测试失败。希望如果我显示测试后跟服务逻辑,它会更清楚我要做什么。

我想我可能遗漏了一些与正确模拟服务有关的注释。我认为模拟服务未连接到其模拟存储库。我不知道要添加哪些特定配置。

代码如下:

测试:

package portal.services;

import static org.junit.Assert.assertEquals;

@RunWith(SpringJUnit4ClassRunner.class)
@DataJpaTest
public class TestRunServiceTest {
    @MockBean
    private TestRunService testRunService;
    @Autowired
    private TestRunRepository testRunRepository;
    @Autowired
    private TestCaseRepository testCaseRepository;
    @Autowired
    private TestToolRepository testToolRepository;
    @Autowired
    private TestSuiteRepository testSuiteRepository;

    TestCase testCase = new TestCase();
    TestTool testTool = new TestTool();
    TestSuite testSuite = new TestSuite();
    TestRun testRun = new TestRun();

    @Before 
    public void createTestData(){
        // create the test case
        testCase.setId(1);
        testCase.setName("Agent Charities");
        testCaseRepository.save(testCase);

        // create test tool
        testTool.setId(1);
        testToolRepository.save(testTool);

        //create test suite
        testSuite.setId(1);
        testSuite.setTestCase(testCase);
        testSuite.setTestTool(testTool);
        testSuiteRepository.save(testSuite);

        // create test run
        testRun.setTestSuite(testSuite);
        testRun.setStartDateTime(new Date());
        testRun.setEndDateTime(new Date());
    }

    @Test
    public void aggregateDataCountWhenResultIsPass(){

        // test run result is true with 5 data items
        testRun.setResult(true);
        testRun.setGeneratedDataCount((long)5);
        testRunService.add(testRun);

        // test run result is false with 3 data items
        testRun.setResult(false);
        testRun.setGeneratedDataCount((long)3);
        testRunService.add(testRun);

        // TEST FAILS BECAUSE testRunService did not persist any data
        System.out.println(testRunRepository.count()); //output = 0

        // test tool and test case repositories should show count of 5
        assertEquals((long)5, testCaseRepository.findByid((long)1).getGeneratedDataCount().longValue());
        assertEquals((long)5, testToolRepository.findByid((long)1).getGeneratedDataCount().longValue());
        // test failed with NullPointerException, GeneratedDataCount was not updated by testRunService
    }

}

服务:

public interface TestRunService {
    Iterable<TestRun> listAllTestRuns();
    TestRun getTestRunById(Integer id);
    ResponseEntity<?> add(@RequestBody TestRun input);
}


@Service
public class TestRunServiceImpl implements TestRunService {

    private TestRunRepository testRunRepository;
    private TestSuiteRepository testSuiteRepository;
    private TestCaseRepository testCaseRepository;
    private TestToolRepository testToolRepository;

    @Autowired
    public void setTestRunRepository(TestRunRepository testRunRepository) {
        this.testRunRepository = testRunRepository;
    }
    @Autowired
    public void setTestSuiteRepository(TestSuiteRepository testSuiteRepository) {
        this.testSuiteRepository = testSuiteRepository;
    }
    @Autowired
    public void setTestCaseRepository(TestCaseRepository testCaseRepository) {
        this.testCaseRepository = testCaseRepository;
    }
    @Autowired
    public void setTesttOOLRepository(TestToolRepository testToolRepository) {
        this.testToolRepository = testToolRepository;
    }

    @Override
    public Iterable<TestRun> listAllTestRuns() {
        return testRunRepository.findAll();
    }

    @Override
    public TestRun getTestRunById(Integer id) {
        return testRunRepository.findOne((long)id);
    }

    @Override
    public ResponseEntity<?> add(@RequestBody TestRun input) {

        // save te test run
        TestRun result = testRunRepository.save(input);
        URI location = ServletUriComponentsBuilder
                .fromCurrentRequest().path("/{id}")
                .buildAndExpand(result.getId()).toUri();

        // update runtime total for test TOOL
        TestSuite suite = testSuiteRepository.findByid(result.getTestSuite().getId());
        TestTool tool = testToolRepository.findByid(suite.getTestTool().getId());
        tool.setTotalRuntime(result.getRuntimeMilliseconds());

        // if the run was a pass, update the generated data count as well
        if (result.getResult() == true){
            tool.setGeneratedDataCount(result.getGeneratedDataCount());
        }
        // save updated test tool information
        testToolRepository.save(tool);

        // update runtime total for test CASE
        TestCase testCase = testCaseRepository.findByid(suite.getTestCase().getId());
        testCase.setTotalRuntime(result.getRuntimeMilliseconds());

        // if the run was a pass, update the generated data count as well
        if (result.getResult() == true){
            testCase.setGeneratedDataCount(result.getGeneratedDataCount());
        }
        // save updated test case information
        testCaseRepository.save(testCase);

        return ResponseEntity.created(location).build();        
    }
}

问题是我知道该服务是通过手动测试和检查数据库来工作的,所以我无法让单元测试通过,这让我很沮丧。

【问题讨论】:

  • 为什么要这样做...您正在创建服务的模拟,因此您的实现中的任何内容都不会被执行...您认为@MockBean 的用途是什么?
  • 嗯,好吧,我肯定需要执行它,所以我该怎么做呢?我试过@Autowired,错误是Error creating bean with name 'portal.services.TestRunServiceTest': Unsatisfied dependency expressed through field 'testRunService'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean found for dependency [portal.services.TestRunService]: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations:
  • 我确实知道答案,但我不想告诉别人答案,而是将他们指向包含答案的资源。有点像我可以给一个人一条鱼或学习他们如何捕鱼。如果必须的话。将@DataJpaTest 替换为@SpringBootTest(并将其指向您的应用程序类),将@MockBean 替换为@Autowired,如果您的spring boot 配置正确(您可能需要将其传递给@SpringBootTest(classes=&lt;your-class-here&gt;)。这将启动一个完整的春季启动测试。这也是我指出的文档中解释的所有内容。

标签: spring-mvc junit spring-boot mockito


【解决方案1】:

我不确定您为什么要为您的服务使用 @MockBean 注释。

您可以尝试在您的设置方法中构造一个适当的 TestRunService 对象并使用它吗?

例如:

@Before 
public void createTestData(){
     //set up service
     testRunService = new TestRunServiceImpl();
     //invoke repository setters on your service object
     //data creation     
}

【讨论】:

  • 谢谢,当我尝试错误时,我得到的是Cannot instantiate the type TestRunService - 我删除了@MockBean 注释。
  • testRunService = new TestRunServiceImpl();
  • 这行不通,因为您没有事务代理。
  • 是的,它正在抛出错误java.lang.NullPointerException at portal.services.TestRunServiceImpl.add(TestRunServiceImpl.java:58) at portal.services.TestRunServiceTest.aggregateDataCountWhenResultIsPass(TestRunServiceTest.java:74) 实现的第 58 行是它第一次尝试访问其存储库的位置。
【解决方案2】:
  1. 使用SpringRunner.class 代替SpringJUnit4ClassRunner.class,并将@MockBean 更改为@Autowired@DataJpaTest 为必填项。

  2. 如果有ContextConfiguationPersistenceConfig等需要的配置需要使用@Import({ContextConfiguation.class,...})导入

  3. 使用@TestConfiguration创建模拟bean

    @TestConfiguration 静态类 TestRunServiceImplTextContextConfig { @豆 公共测试运行服务 testRunService() { 返回新的 TestRunServiceImpl(); } }

我使用 Spring Boot 2.1.1 进行了测试。

【讨论】:

    猜你喜欢
    • 2019-12-06
    • 2015-11-29
    • 2018-11-09
    • 1970-01-01
    • 2021-03-15
    • 1970-01-01
    • 2018-12-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多