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

在完成了数据访问层的单元之后,接下来看如何编写服务层(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);

}

}

  其实服务层的测试并没有太多的新东西,而最关键的问题是如何把逻辑中各个分支都能测试到,使测试真正起到为软件质量保驾护航的作用。

最新内容请见作者的GitHub页:http://qaseven.github.io/

时间: 2024-12-10 21:04:00

持续集成之路—服务层的单元测试的相关文章

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

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

如何破局CI工具拉锯战:探寻中小企业的持续集成之路

摘要:随着持续集成技术的不断成熟,各种持续集成相关的开源和商用软件层出不穷,但是对于中小型企业的技术团队而言,往往在进行持续集成实践时会陷入工具之间的拉锯战.那么,对于中小团队而言,如何才能实现高效敏捷的持续集成方案?2017苏州云栖大会云效专场上,南京路特CTO.阿里云MVP戚俊结合实践经验为大家分享了中小团队持续集成之路. 以下内容根据演讲视频以及PPT整理而成. 虽然持续集成的概念已经火了很长时间了,但是因为各个企业的规模以及业务类型都不同,所以在持续集成中遇到的问题也各不相同.本次将结合

持续集成之路——数据访问层的单元测试

        翻看之前的文章才发现,最近一次记录持续集成竟然是3年前,并且只记录了两篇,实在是惭愧.不过,持续集成的这团火焰却始终在心中燃烧,希望这次的开始可以有些突破.          测试是持续集成的基石,没有测试的集成基本上是毫无意义的.如何写好测试就是横亘在我面前的第一个问题.那就从数据访问层开始吧.说起来可笑,从3年前第一次准备做持续集成式,就开始考虑测试数据访问层的一些问题: 难道我要在测试服务器上装一个MySQL? 数据库结构发生了变化怎么办? 怎么样才能消除测试间的依赖? 测

持续集成之路——Maven

   写在前面       自从参加了4月份的QCon以后,持续集成的念头一直在脑海中浮现.时隔四个月之后,终于有了实践的机会.在此,首先应该感谢头儿们的大力支持.记得参加Scrum的培训时,最开始提到的一个障碍就是来自上层.因为上层希望看到的是实实在在的成果,如果现有的开发模式和质量管理系统已经有了比较好的效果,他们就不太愿意尝试新的模式.而这次正是有了头儿们的理解和支持,我才能放开手脚."无所顾忌"地尝试这些新手段.其次,应该感谢我的天才同事们.因为,每次即使小小的改动都会给他们的

持续集成之路——数据访问层单元测试遇到的问题

在编写数据访问层的单元测试时,遇到不少问题,有些问题可以很容易Google到解决方法,而有些只能自己研究解决.这里分享几个典型的问题以及解决方法. 先交代一下用到的测试框架 Spring Test + SpringTestDbUnit + DbUnit. 一.先说一个低级的问题. Spring通过<jdbc:embedded-database>标签提供对内存数据的支持,形如: <jdbc:embeded-database id="dataSource" type=&q

持续集成之路——数据访问层的单元测试(续)

        在上一篇中,完成了对测试用数据源的配置.下面继续构建可运行的测试.        三.使用DBUnit管理数据         测试的维护一直是我比较头疼的问题,期望可以有一个比较易于维护和可复用的方法来管理这些数据.在没有更好的方法之前,暂时选用DBUnit.(反思:其实我一直在为没有发生的事情担心,使得事情根本没有进展.从已存在的.最简单的地方入手,才是正确的处理方式.)         在pom.xml中引入dbunit和springtestdbunit包,后者提供通过注解

持续集成之路——Maven(续)

(接上篇)在新创建了项目之后,可以看到目录结构:         从图中可以看出,Maven自动将项目源文件和测试用例分别放到了不同的目录下,但是却使用了相同的包名.这样生成可执行的产品时,就比较容易将产品代码和测试代码隔离开:同时测试用例也可以直接引用同一包中被测试的类,既符合了JUnit的规范,也使程序更易读.       正如上图中标示,新建项目的JRE默认为J2SE-1.4,这会导致1.5之后的一些新特性得不到支持.可以通过下面的方法修改:           2.使用Maven管理依赖

持续集成之路——搭建Maven私服

      在开发过程中,有时候会使用到公司内部的一些开发包,显然把这些包放在外部是不合适的.另外,由于项目一直在开发中,这些内部的依赖可能也在不断的更新.可以通过搭建公司内部的Maven服务器,将第三方和内部的依赖统一管理.       这里使用Nexus来搭建本地的Maven服务器,过程比较简单.         一.安装服务器       1.下载       我们可以在nexus的官网上找到它的相关介绍,下载地址是:http://nexus.sonatype.org/downloads/

产品迭代发布如何更快速?阿里持续集成与持续交付实践之路全解析

2017年5月9日,云效平台资深研发工程师向禹通过直播分享了<持续集成与持续交付实践之路>.他从云效背景.云效方案.云效价值三个方面进行了分享.他主要分享了持续集成持续交付的解决方案和案例,并且对大型系统如何实现持续集成.持续交付.进行产品迭代发布进行了详细介绍. 以下内容根据直播视频整理而成. 云效背景--阿里巴巴<持续交付>之路 大应用下的交付 在七八年之前,阿里巴巴的B2B一直沿用瀑布的模式来进行项目管理,当时已经感觉到瀑布模式对应用持续快速的发展产生了很大的影响.并且当时很