持续集成之路——服务层的单元测试

简介:         在完成了数据访问层的单元之后,接下来看如何编写服务层(Service)的单元测试。服务层应该是整个系统中得重中之重,严密的业务逻辑设计保证了系统稳定运行,所以这一层的单元测试也应该占很大比重。

        在完成了数据访问层的单元之后,接下来看如何编写服务层(Service)的单元测试。服务层应该是整个系统中得重中之重,严密的业务逻辑设计保证了系统稳定运行,所以这一层的单元测试也应该占很大比重。虽然一般情况下单元测试应该尽量通过mock剥离依赖,但是由于在当前的项目中数据访问层使用spring-data框架,并没有包含太多的逻辑,因此我就把服务层和数据访问层放在做了一个伪单元测试。

        一、一般逻辑的单元测试。

        这里采用的方式和数据访问层几乎是一样的,主要包含三步:

        1. 通过@DatabaseSetup指定测试用数据集

        2. 执行被测试方法

        3. 通过Dao从数据库中查询数据验证执行结果

        假设要被测试的代码方法是:

@Service
@Transactional(readOnly = true)
public class ShopServiceImpl extends BaseService implements ShopService{
    private Logger logger = LoggerFactory.getLogger(ShopServiceImpl.class);

    @Transactional(readOnly = false)
    public Floor addFloor(String buildingName, int floorNum, String layout) {
        //如果已经存在对应的楼层信息,则抛出已经存在的异常信息
        Floor floor = floorDao.findByBuildingNameAndFloorNum(buildingName, floorNum);
        if (floor != null) {
            throw new OnlineShopException(ExceptionCode.Shop_Floor_Existed);
        }
        
        //如果不存在对应的商场信息,则添加新的商场
        Building building = buildingDao.findByName(buildingName);
        if (building == null) {
            building = new Building();
            building.setName(buildingName);
            buildingDao.save(building);
        }
 
        //添加并返回楼层信息
        floor = new Floor();
        floor.setBuilding(building);
        floor.setFloorNum(floorNum);
        floor.setMap(layout);

        floorDao.save(floor);

        return floor;
    }
}

其对应的接口是:

public interface ShopService {
    public Floor addFloor(String buildingName, int floorNum, String layout);
}

        这段逻辑代码的意思十分简单和直白,那么要编写的单元的测试必须要包含所有分支情况:a. 商场和楼层信息都存在的,抛出异常 b. 商场存在,而楼层不存在, 楼层信息都被添加的。 c.  商场和楼层都不存在,全部新增。这里就以第一种情况为例,先准备测试数据:

<?xml version="1.0" encoding="UTF-8"?>
<dataset>
    <building id="1" name="New House"/>
    <floor id="1" building="1" floor_num="2"/>
</dataset>

接着编写测试用例,注意要必须得注解不能忘掉:

  

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext-test.xml")
@Transactional
@TestExecutionListeners({
        DependencyInjectionTestExecutionListener.class,
        DirtiesContextTestExecutionListener.class,
        CustomTransactionDbUnitTestExecutionListener.class,
        ForeignKeyDisabling.class})
public class ShopServiceTest {
    @Autowired
    private ShopService shopService;

    @Test
    @DatabaseSetup("shop/ShopService-addFloorExistException-dataset.xml")
    public void testAddFloorExistException(){
        try {
            shopService.addFloor("New House", 2, "");
            fail();
        } catch(Exception e){
            assertTrue(e instanceof OnlineShopException);
            assertEquals(ExceptionCode.Shop_Floor_Existed.code(), ((OnlineShopException)e).getCode());
        }
    }

}

这个测试和数据访问层的测试看起来没有什么两样。

        二、使用Mock对象隔离第三方接口

        软件开发中一般都存在和第三方集成的情况,比如调用新浪的认证、百度的地图等等。那么在编写测试的时候,基于效率的考虑,一般情况不会真的去调用这些远程API(当然应该有其他测试可以及时发现第三方接口的变化),而是假定它们一直会返回预期的结果。这个时候就需要用到mock对象,来模拟这些API产生相应的结果。

        在这里,我是用了mockito,使用十分方便。假如现在用户登录时,需要去第三方系统验证,那么现在来看如何对这个场景进行测试。还是先来看被测试的方法:

private boolean validateUser(String inputName, String inputPassword) {
        return thirdPartyAPI.authenticate(inputName, inputPassword);
}

 其中thirdPartyAPI就是第三方用来认证的API。下面来看测试代码:

public class UserServiceTest {
    @Autowired
    private UserService userService;
    private ThirdPartyAPI mockThirdPartyAPI = mock(ThirdPartyAPI.class);
    
    @Test
    public void testLogin(){
        //指定mock对象特定操作的返回结果
        when(mockThirdPartyAPI.authenticate("jiml", "jiml")).thenReturn(true);
        //通过Setter用mock对象替换由Spring初始化的第三方依赖
        ((UserServiceImpl)userService).setThirdPartyAPI(mockThirdPartyAPI);
        boolean loginStatus = userService.login("jiml", "jiml");
        assertTrue(loginStatus);
    }
}

      其实服务层的测试并没有太多的新东西,而最关键的问题是如何把逻辑中各个分支都能测试到,使测试真正起到为软件质量保驾护航的作用。
目录
相关文章
|
9月前
|
存储 人工智能 测试技术
小鱼深度评测 | 通义灵码2.0,不仅可跨语言编码,自动生成单元测试,更炸裂的是集成DeepSeek模型且免费使用,太炸裂了。
小鱼深度评测 | 通义灵码2.0,不仅可跨语言编码,自动生成单元测试,更炸裂的是集成DeepSeek模型且免费使用,太炸裂了。
141591 29
小鱼深度评测 | 通义灵码2.0,不仅可跨语言编码,自动生成单元测试,更炸裂的是集成DeepSeek模型且免费使用,太炸裂了。
|
敏捷开发 测试技术 持续交付
探索软件测试中的自动化与持续集成
在快速迭代的软件开发环境中,自动化测试和持续集成(CI)已成为确保产品质量和加速交付的关键策略。本文将深入探讨自动化测试的基本原理、实施步骤以及它如何与持续集成流程相结合,以提高软件开发的效率和可靠性。我们将通过实际案例分析,展示自动化测试和CI的最佳实践,以及它们如何帮助企业实现更快的市场响应时间和更高的客户满意度。
200 16
|
jenkins 测试技术 持续交付
软件测试中的自动化与持续集成:提升效率与质量的关键
在快节奏的软件开发环境中,自动化测试和持续集成已经成为不可或缺的部分。本文将探讨自动化测试和持续集成的重要性,以及它们如何协同工作以提高软件开发的效率和质量。通过分析自动化测试的策略、工具选择以及持续集成的实践,我们将揭示这些技术如何帮助开发团队快速响应变化,减少错误,并加速产品上市时间。
|
机器学习/深度学习 人工智能 jenkins
软件测试中的自动化与持续集成实践
在快速迭代的软件开发过程中,自动化测试和持续集成(CI)是确保代码质量和加速产品上市的关键。本文探讨了自动化测试的重要性、常见的自动化测试工具以及如何将自动化测试整合到持续集成流程中,以提高软件测试的效率和可靠性。通过案例分析,展示了自动化测试和持续集成在实际项目中的应用效果,并提供了实施建议。
|
缓存 Devops jenkins
专家视角:构建可维护的测试架构与持续集成
【10月更文挑战第14天】在现代软件开发过程中,构建一个可维护且易于扩展的测试架构对于确保产品质量至关重要。本文将探讨如何设计这样的测试架构,并将单元测试无缝地融入持续集成(CI)流程之中。我们将讨论最佳实践、自动化测试部署、性能优化技巧以及如何管理和扩展日益增长的测试套件规模。
229 3
|
11月前
|
jenkins 测试技术 持续交付
软件测试中的自动化与持续集成
在现代软件开发过程中,自动化测试和持续集成已成为不可或缺的组成部分。本文将深入探讨自动化测试和持续集成的重要性、优势以及如何有效实施它们以提升软件质量和开发效率。通过具体案例分析,我们将展示这些技术如何在实际项目中发挥作用,并讨论其面临的挑战及应对策略。
292 60
|
9月前
|
人工智能 IDE 测试技术
用户说 | 通义灵码2.0,跨语言编码+自动生成单元测试+集成DeepSeek模型且免费使用
通义灵码, 作为国内首个 AI 程序员,从最开始的内测到公测,再到通义灵码正式发布第一时间使用,再到后来使用企业定制版的通义灵码,再再再到现在通义灵码2.0,我可以说“用着”通义灵码成长的为数不多的程序员之一了吧。咱闲言少叙,直奔主题!今天,我会聊一聊通义灵码的新功能和通义灵码2.0与1.0的体验感。
|
9月前
|
人工智能 IDE 测试技术
用户说 | 通义灵码2.0,跨语言编码+自动生成单元测试+集成DeepSeek模型且免费使用
用户说 | 通义灵码2.0,跨语言编码+自动生成单元测试+集成DeepSeek模型且免费使用
|
11月前
|
机器学习/深度学习 人工智能 jenkins
探索软件测试中的自动化与持续集成
【10月更文挑战第21天】 在软件开发的生命周期中,软件测试扮演着至关重要的角色。随着技术的进步和开发模式的转变,自动化测试和持续集成已经成为提高软件质量和效率的关键手段。本文将深入探讨自动化测试和持续集成的概念、实施策略以及它们如何相互配合以优化软件开发流程。我们将通过分析实际案例,展示这些技术如何在实际项目中发挥作用,以及面临的挑战和解决方案。此外,文章还将讨论未来趋势,包括人工智能在测试领域的应用前景。
295 17
|
12月前
|
Devops 测试技术 持续交付
软件测试中的自动化与持续集成:最佳实践与挑战
在快速迭代的软件开发周期中,自动化测试和持续集成(CI)已成为提高软件质量和加速产品上市的关键策略。本文探讨了自动化测试和CI的实施如何帮助开发团队提前发现缺陷、缩短反馈循环,并确保代码质量。我们将深入分析自动化测试的策略选择、工具应用以及面临的挑战,同时提供一些克服这些挑战的最佳实践。
384 27