`
run_xiao
  • 浏览: 194853 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
社区版块
存档分类
最新评论

JUnit测试的粒度问题

阅读更多
对于JUnit测试和TDD实践中有如下的疑问,请各位解惑:
JUnit测试的粒度如何把握?
简单的说是针对public的方法写测试就OK了呢?还是说要具体针对public方法中执行逻辑的每个步骤来写测试方法?
先说一下为什么会有这种困惑:
业务逻辑比较简单时,当然只针对Public方法的业务流程来设计案例,并只对public方法写test方法就好。
但最近做一个保险的项目,计算超复杂的那种,用户点一个Button后台要操作十几张表,数据Copy来Copy去
中间还有各种各样的计算,设计的业务Interface方法中接受User的输入,然后执行整个操作。
现在谈一下两种实现的方式:
1.按TDD的方式,先写测试代码,再写实现代码,实现过程不断重构(未完整了解过TDD,只是皮毛,如有误解见谅)
这种方式实现起来很有难度。首先测试代码的覆盖度很难保证:当复杂的业务逻辑揉在一个方法中(即使重构拆成若干小方法),流程分支成幂增长,很难一开始就把所有的情形都考虑清楚,即使都考虑到了,写出来的TestCase也可能是超复杂的,反而会成为一种负担。
另外,这样来做实际上也就相当于大块大块的Coding,然后测试,偏离了TDD的本意,Coding过程中没办法保证做的每一步都是正确的,而是将这个测试推迟到完成了整个实现之后。
2.对整个业务逻辑的实现大致上先分为几个步骤,每个步骤的实现可以放在protected方法中以便测试,然后再针对每一步来实践TDD,这样没有上述的两个问题,而且最终程序员对自己代码的信心会大增。但这样来做也有一些问题。
首先,每一步骤的方法都是protected才能保证测试,这样破坏了封装
其次,测试代码是针对接口实现的过程来写的,而不是针对接口的功能,所以测试代码可能会很脆弱,实现过程稍作变化测试代码也可能要做修改

所以,最根本的问题也就是单元测试是应该针对接口实现的过程还是接口的功能?
分享到:
评论
17 楼 yiding_he 2007-08-08  
allentemplar 写道
主要是层次的清晰
单讨论测试没有多大意义

没错,有了松耦合的设计,单元测试自然不成问题。
16 楼 linqw 2007-08-08  
allentemplar 写道
主要是层次的清晰
单讨论测试没有多大意义

我赞同这个观点。
15 楼 allentemplar 2007-07-19  
主要是层次的清晰
单讨论测试没有多大意义
14 楼 run_xiao 2007-07-09  
最近又考虑了一下该问题,
UT应该是对接口的实现做白盒测试

若接口的实现巨复杂,那就要Refactor,将其职责分担到其他的Class,
这样再对其他Class作UT
13 楼 kjhot 2007-03-22  
仔细阅读《敏捷软件开发》,会找到一些你要的东西!
12 楼 hyysguyang 2007-03-22  
重构,让它易于测试.这就是单元测试的反馈的一个非常重要的优点.实际上也许你的对象已经是一个GOD CLASS了,遵循单一职责.
还是那句话,重构,让它易于测试.
11 楼 lane_cn 2007-03-20  
run_xiao 写道
经理要看的并不是简单的客户的资料。客户有很多笔交易,每笔交易都有金额,每笔交易都有期望的利润率。经理要看我这个月准备从每个户赚多少的钱的Report。
如何做UT?

这不是UT应该怎样写的问题,而是设计应该是什么样。我对你所说的业务不了解,暂且按照我心目中的设计写一个UT:

首先为这个“客户”建立一个“交易”;
为这个“交易”设定参数:时间、地点、人物、“项目”、“成交价”……
“交易”“执行”;
Assert(“客户”的“交易历史”上出现这笔“交易”);
Assert(“交易”的“金额”符合刚才设定的参数);
Assert(“交易”的“利润率”符合刚才设定的参数);
Assert(从“客户”身上赚的钱符合刚才设定的参数);
Assert(经理希望看见的“Report”上面的数据符合刚才设定的参数);
10 楼 run_xiao 2007-03-19  
引用

我把这个情况更加具体的说明一下。设想有一个系统,他有两个功能:
1、操作员在前台界面上输入客户的资料;
2、经理在后台界面上查询客户的资料。

经理要看的并不是简单的客户的资料。客户有很多笔交易,每笔交易都有金额,每笔交易都有期望的利润率。经理要看我这个月准备从每个户赚多少的钱的Report。
如何做UT?
9 楼 lane_cn 2007-03-19  
你对单元测试粒度的问题,说到底其实不是测试的问题,而是一个设计的问题。在一个内聚性良好的系统里你的问题自然是不会成立的。

“假如A负责把数据放进去,A也要负责把数据拿出来,总之这些数据的存储和解释完全应该由A来负责。这样就不必测试A的过程了,只需要测试A的接口。即使A的过程根本不对,数据完全存错了,反正他看起来还是一个A,内部错翻天了也影响不到别的模块。”

我把这个情况更加具体的说明一下。设想有一个系统,他有两个功能:
1、操作员在前台界面上输入客户的资料;
2、经理在后台界面上查询客户的资料。

按照这个功能点,一个程序员设计出了这样的代码,他有两个对象:
1、对象A负责接受操作员的输入,把资料拼成一个SQL,在数据库上执行,把数据保存到数据库表里去;
2、对象B负责接受经理的输入,把查询资料拼成一个SQL,在数据库上执行,把用户资料查出来显示。

对于这样的代码,要进行单元测试的话,肯定要深入A和B的细节,最终要测试每一种输入输出在数据库里面的数据是不是正确,或者是产生的SQL是不是正确。
这样就会产生楼主所问的问题。

如果换一种内聚性比较好设计,应该有一个Customer对象,由他负责客户资料的存储和读取,并确负责客户能够操作的所有行为。这样就不必测试Customer的执行过程了,只需要关心他的接口。

可以有这样的测试用例:把一个姓名字符串设置到Customer对象上,然后得到这个对象的姓名,他应该是刚才设置的字符串。这就测试了Customer对象的Name属性。至于这个Name是不是真的存储到了数据表的某个字段里,这个不需要测试,即使需要测试也应该通过接口的表现来测试(比如我们可以用同样的ID再得到这个Customer对象,观察他的Name属性),而不应该去深入他的实现细节,看他的SQL拼写的对不对。

即使不测实现过程也没有关系,Customer即使没有正确的保存到数据库里,只要接口是对的,他用起来仍然是一个正确的Customer,他的内部怎么实现是他自己的事情。
8 楼 run_xiao 2007-03-19  
引用
单元测试重在细粒度,它不是用来测试一个包,也不是用来测试一个类,而是用来测试一个一个的方法。如果一个测试把整个包十几个类都走了一遍,出了问题也很难找到在那。所以说几乎是用来摆看的。

Of course,JUNIT是用来测试每个方法,但也只能测试public的方法,顶多也还可以算上protected。
没这点共识就没法做UT了。
引用

单元测试就是白盒测试,把所有的单元测试运行一道,我就知道系统的每一小步每一小步是不是都走得稳。如果一个方法仅仅是用来转换日期格式,它的单元测试又能有多复杂呢?

如果我一个public方法要实现的业务逻辑巨复杂,你说的方式就是我提到的第二种方法,这实际上并非白盒测试,因为已经深入到该方法实现的过程中了,是对每一步的测试
7 楼 yiding_he 2007-03-16  
run_xiao 写道
引用
如果一个测试几乎是用来摆看的,那就把它去掉。

针对接口的功能来测试的话,是白盒测试
对接口的实现过程来的测试话,是黑盒测试
两种都是有其意义的,我想问的是两个方式对Coding来说更方便
楼上说的用来摆设的测试,能否说清楚一点?


单元测试重在细粒度,它不是用来测试一个包,也不是用来测试一个类,而是用来测试一个一个的方法。如果一个测试把整个包十几个类都走了一遍,出了问题也很难找到在那。所以说几乎是用来摆看的。单元测试就是白盒测试,把所有的单元测试运行一道,我就知道系统的每一小步每一小步是不是都走得稳。如果一个方法仅仅是用来转换日期格式,它的单元测试又能有多复杂呢?
6 楼 run_xiao 2007-03-16  
引用
如果一个测试几乎是用来摆看的,那就把它去掉。

针对接口的功能来测试的话,是黑盒测试
对接口的实现过程来的测试话,是白盒测试
两种都是有其意义的,我想问的是两个方式对Coding来说更方便
楼上说的用来摆设的测试,能否说清楚一点?
5 楼 yiding_he 2007-03-16  
simohayha 写道
测试永远是针对功能进行测试.


没错,绝对不要为了测试而测试。如果一个测试几乎是用来摆看的,那就把它去掉。
4 楼 simohayha 2007-03-13  
测试永远是针对功能进行测试.
3 楼 lane_cn 2007-03-13  
run_xiao 写道
lane_cn 写道
假如A负责把数据放进去,A也要负责把数据拿出来,总之这些数据的存储和解释完全应该由A来负责。这样就不必测试A的过程了,只需要测试A的接口。即使A的过程根本不对,数据完全存错了,反正他看起来还是一个A,内部错翻天了也影响不到别的模块。

这也许是涉及Requirement的问题了,假设就有这样复杂的Req,比如不同粒度的数据转换

这个和req有什么关系,完全是设计问题。
2 楼 run_xiao 2007-03-13  
lane_cn 写道
假如A负责把数据放进去,A也要负责把数据拿出来,总之这些数据的存储和解释完全应该由A来负责。这样就不必测试A的过程了,只需要测试A的接口。即使A的过程根本不对,数据完全存错了,反正他看起来还是一个A,内部错翻天了也影响不到别的模块。

这也许是涉及Requirement的问题了,假设就有这样复杂的Req,比如不同粒度的数据转换
1 楼 lane_cn 2007-03-13  
问题不是出在测试上,而是出在设计上。
你们的设计中一定有很多这样的情况:模块A把数据放到一堆数据表里去,然后模块B从这一堆数据表里面取数据做工作。于是你必须测试A实现这个功能的过程。
好一点的设计是,假如A负责把数据放进去,A也要负责把数据拿出来,总之这些数据的存储和解释完全应该由A来负责。这样就不必测试A的过程了,只需要测试A的接口。即使A的过程根本不对,数据完全存错了,反正他看起来还是一个A,内部错翻天了也影响不到别的模块。
单元测试并不是可以无条件实行的,良好的设计,清晰的层次是必须的。对一团乱麻搞单元测试,人基本要累死。

相关推荐

    Junit测试高级应用

    单元测试是一个最小粒度的测试,以测试某个功能或代码块。一般由程序员来做,因为它需要知道内部程序设计和编码的细节。 JUnit 也是一个高级的单元测试框架,具有很多优点,例如可以使测试代码与产品代码分开、针对...

    软件测试技术JUnit和单元测试入门简介

    JUNIT软件测试软件测试技术JUnit和单元测试入门简介软件测试1、几个相关的概念白盒测试——把测试对象看作一个打开的盒子,程序内部的逻辑结构和其他信息对测试人员是公开的。回归测试——软件或环境的修复或更正后...

    SpringBoot使用Junit进行单元测试

    在Spring Boot应用中,单元测试是一项至关重要的任务,它能够帮助开发者确保代码的质量,提前发现潜在问题,并且便于持续集成和自动化测试。Junit作为Java领域最常用的单元测试框架,与Spring Boot结合使用,可以...

    Junit实战第二版

    由浅入深、由易到难地对JUnit展开了系统的讲解,包括探索JUnit的核心、软件测试原则、测试覆盖率与开发、使用stub进行粗粒度测试、使用mock objects进行测试、容器内测试、从Ant中运行JUnit测试、从Maven2中运行...

    Junit5.7.2离线jar

    通过使用@ExtendWith(SpringExtension.class)注解,我们可以开启Spring的测试支持,利用@SpringBootTest注解启动一个完整的Spring应用上下文,或者使用@WebMvcTest、@DataJpaTest等更细粒度的注解来隔离测试。...

    Junit5依赖整合包

    4. **测试注解增强**:如`@Test`, `@Before`, `@After`等被替换为`@Test`, `@BeforeEach`, `@AfterEach`,提供了更细粒度的控制。 5. **动态测试**:使用`DynamicContainer`和`DynamicTest`,可以在运行时动态生成...

    Junit实战(第2版)

    由浅入深、由易到难地对JUnit展开了系统的讲解,包括探索JUnit的核心、软件测试原则、测试覆盖率与开发、使用stub进行粗粒度测试、使用mockobjects进行测试、容器内测试、从Ant中运行JUnit测试、从Maven2中运行JUnit...

    如何利用JUnit进行单元测试.ppt

    JUnit单元测试框架 本文总结了JUnit单元测试框架的基本概念和使用方法,涵盖了JUnit的介绍、单元测试的概念、JUnit...答:JUnit测试的思想就是在测试代码中设定某一输入值,将其作为输出值,并且验证输出值是否正确。

    Junit4.5.rar

    7. **Test Rules**: `@Rule`注解允许定义测试规则,如`ExpectedException`、`ExternalResource`等,它们提供了更细粒度的控制,可以在测试开始前、结束后以及测试过程中执行自定义操作。 junit_license.txt文件通常...

    在软件测试中了解JUnit和单元测试入门简介

    JUNIT软件测试在软件测试中了解JUnit和单元测试入门简介1、几个相关的概念白盒测试——把测试对象看作一个打开的盒子,程序内部的逻辑结构和其他信息对测试人员是公开的。回归测试——软件或环境的修复或更正后的...

    Junit4.8.1.rar

    对于那些需要更细粒度控制测试执行流程的场景,JUnit4引入了测试规则(Test Rules)。通过实现`org.junit.rules.TestRule`接口,可以定义自定义的规则,这些规则会在每个测试方法执行前后被调用。 总的来说,Junit...

    JUnit_in_Action,_2nd_Edition.pdf

    - **第8章:使用Ant运行JUnit测试**:展示如何配置Ant来自动化JUnit测试流程。 - **第9章:使用Maven 2运行JUnit测试**:介绍如何利用Maven 2进行测试管理。 - **第10章:持续集成工具**:讨论持续集成工具如何与...

    junit4.8.2-javadoc.rar

    在JUnit中,测试类和测试方法可以继承和覆盖注解,提供更细粒度的控制。 八、注解的使用示例 以下是一个简单的测试类示例: ```java import org.junit.Before; import org.junit.Test; public class MyTest { @...

Global site tag (gtag.js) - Google Analytics