避免在Service层写脆弱的测试

本文介绍了一种隔离测试Java Service层的方法,通过Mockito框架模拟DAO/DAL层,验证Service层与数据库的交互逻辑,提高测试的弹性和灵活性。

转自:黑客日

编译:https://www.baeldung.com/testing-the-java-service-layer

 

1、简述

有很多方法可以测试Service层。本文的目标是展示一种隔离的测试方式:将于数据库交互的整个逻辑都Mock掉。

示例使用了Spring作为依赖注入、JUnit、Hamcrest、Mockito。

 

2、分层

典型的Java web应用都在DAO/DAL层之上有一个服务层,DAO/DAL层负责调用原生的持久层逻辑。

 

Service层

01

02

03

04

05

06

07

08

09

10

11

12

@Service

public class FooService implements IFooService{

 

@Autowired

IFooDAO dao;

 

@Override

public Long create( Foo entity ){

return this.dao.create( entity );

}

 

}

DAO/DAL层

01

02

03

04

05

06

07

08

09

10

11

12

13

@Repository

 

public class FooDAO extends HibernateDaoSupport implements IFooDAO{

 

public Long create( Foo entity ){

 

Preconditions.checkNotNull( entity );

 

return (Long) this.getHibernateTemplate().save( entity );

 

}

 

}

3、单元测试的动机和关注点

当单元测试的对象是一个服务的时候,“单元”就是Service类,测试需要模拟Service其下的层,这里就是模拟DAO/DAL层,并验证与之的交互操作。

同样地,对DAO/DAL层的单元测试来说,要模拟掉数据库层(本例中就是HibernateTemplate),并验证相关交互操作。

 

这个方法是 有效的,但是这样的测试是脆弱的。增加或移除一层的话,就需要完全重写测试。因为测试依赖了层的具体结构,层的变化就意味着测试的变化。

 

为了避免这种不灵活,我们通过延展“单元”的定义从而扩展单元测试的范围。我们可以把Service层通过DAO和其他方式穿透到持久层的操作看做一个“单元”。单元测试就针对Service层的API,模拟掉原生的持久化逻辑。本例里面就是HibernateTemplate。

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

public class FooServiceUnitTest{

  

   FooService instance;

  

   private HibernateTemplate hibernateTemplateMock;

  

   @Before

   public void before(){

      this.instance = new FooService();

      this.instance.dao = new FooDAO();

      this.hibernateTemplateMock = mock( HibernateTemplate.class );

      this.instance.dao.setHibernateTemplate( this.hibernateTemplateMock );

   }

  

   @Test

   public void whenCreateIsTriggered_thenNoException(){

      // When

      this.instance.create( new Foo( "testName" ) );

   }

  

   @Test( expected = NullPointerException.class )

   public void whenCreateIsTriggeredForNullEntity_thenException(){

      // When

      this.instance.create( null );

   }

  

   @Test

   public void whenCreateIsTriggered_thenEntityIsCreated(){

      // When

      Foo entity = new Foo( "testName" );

      this.instance.create( entity );

  

      // Then

      ArgumentCaptor< Foo > argument = ArgumentCaptor.forClass( Foo.class );

      verify( this.hibernateTemplateMock ).save( argument.capture() );

      assertThat( entity, is( argument.getValue() ) );

   }

  

}

现在测试只关注Service类的一个职责:当创建被触发,相关操作是否传递到了数据库。

例子里面用Mockito框架的验证语法来检查HiebernateTemplate的save方法是否被调用。测试信任hibernate的保存逻辑,因此创建实体的职责只需要像这样验证save方法是否被调用。

当然保存逻辑也需要被测试,但是这是其他测试的范畴。

4、结论

这种方式会产出更多具有弹性和灵活性的测试。测试失败唯一的原因就是被测试逻辑之下的职责失败了。比如HibernateTemplate的save方法。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值