常规单元测试和存根–测​​试技术4

我的上一个博客是有关测试代码的方法以及讨论您要做和不需要进行测试的方法的一系列博客中的第三篇。 它基于我使用一种非常常见的模式从数据库中检索地址的简单方案:

…并且我提出了这样的想法:任何不包含任何逻辑的类都不需要进行单元测试。 在其中,我包括了我的数据访问对象DAO,而不是对此类进行集成测试以确保其与数据库协同工作。

今天的博客涵盖编写常规或经典的单元测试,该测试使用存根对象强制实现测试主题隔离。 我们将要测试的代码再次是AddressService

@Component
public class AddressService {private static final Logger logger = LoggerFactory.getLogger(AddressService.class);private AddressDao addressDao;/*** Given an id, retrieve an address. Apply phony business rules.* * @param id*            The id of the address object.*/public Address findAddress(int id) {logger.info("In Address Service with id: " + id);Address address = addressDao.findAddress(id);address = businessMethod(address);logger.info("Leaving Address Service with id: " + id);return address;}private Address businessMethod(Address address) {logger.info("in business method");// Apply the Special Case Pattern (See MartinFowler.com)if (isNull(address)) {address = Address.INVALID_ADDRESS;}// Do some jiggery-pokery here....return address;}private boolean isNull(Object obj) {return obj == null;}@Autowired@Qualifier("addressDao")void setAddressDao(AddressDao addressDao) {this.addressDao = addressDao;}
}

迈克尔·费瑟(Michael Feather)的书《有效使用旧版代码》指出,在以下情况下,测试不是单元测试:

  1. 它与数据库对话。
  2. 它通过网络进行通信。
  3. 它涉及文件系统。
  4. 您必须对环境做一些特殊的事情(例如编辑配置文件)才能运行它。

为了遵守这些规则,您需要将测试对象与系统的其余部分隔离开来,这就是存根对象的所在。存根对象是注入到您的对象中的对象,用于在测试情况下替换实际对象。 马丁·福勒(Martin Fowler)在他的论文《 莫克斯不是存根》中将存根定义为:

“存根提供对测试过程中进行的呼叫的固定答复,通常通常根本不响应测试中编程的内容。 存根还可以记录有关呼叫的信息,例如电子邮件网关存根,它可以记住“已发送”的消息,或者仅记住“已发送”的消息数量。”

用一个单词来描述存根非常困难,我可以选择虚拟或伪造,但是有些替换对象称为假人或伪造-也由Martin Fowler描述:

  • 虚拟对象会传递,但从未实际使用过。 通常它们仅用于填充参数列表。
  • 伪对象实际上具有有效的实现,但是通常采取一些捷径,这使它们不适合生产(内存数据库是一个很好的例子)。

但是,我看到过其他术语“伪造对象”的定义,例如Roy Osherove在《 The Art Of Unit Testing》一书中将伪造对象定义为:

  • 伪造品是一个通用术语,可用于描述存根或模拟对象……因为两者看起来都像真实对象。

…因此,我和其他许多人一样,倾向于将所有替换对象称为模拟或存根,因为两者之间存在差异,但稍后会更多。

在测试AddressService时 ,我们需要用存根数据访问对象替换实际的数据访问对象,在这种情况下,它看起来像这样:

public class StubAddressDao implements AddressDao {private final Address address;public StubAddressDao(Address address) {this.address = address;}/*** @see com.captaindebug.address.AddressDao#findAddress(int)*/@Overridepublic Address findAddress(int id) {return address;}
}

注意存根代码的简单性。 它应该易于阅读,可维护,并且不包含任何逻辑,并且需要自己进行单元测试。 编写存根代码后,接下来进行单元测试:

public class ClassicAddressServiceWithStubTest {private AddressService instance;@Beforepublic void setUp() throws Exception {/* Create the object to test *//* Setup data that's used by ALL tests in this class */instance = new AddressService();}/*** Test method for* {@link com.captaindebug.address.AddressService#findAddress(int)}.*/@Testpublic void testFindAddressWithStub() {/* Setup the test data - stuff that's specific to this test */Address expectedAddress = new Address(1, "15 My Street", "My Town","POSTCODE", "My Country");instance.setAddressDao(new StubAddressDao(expectedAddress));/* Run the test */Address result = instance.findAddress(1);/* Assert the results */assertEquals(expectedAddress.getId(), result.getId());assertEquals(expectedAddress.getStreet(), result.getStreet());assertEquals(expectedAddress.getTown(), result.getTown());assertEquals(expectedAddress.getPostCode(), result.getPostCode());assertEquals(expectedAddress.getCountry(), result.getCountry());}@Afterpublic void tearDown() {/** Clear up to ensure all tests in the class are isolated from each* other.*/}
}

请注意,在编写单元测试时,我们的目标是清晰。 通常会犯一个错误,就是认为测试代码不如生产代码,从而导致测试代码更加混乱且难以辨认。 单元测试的艺术中的 Roy Osherove提出了这样的想法,即测试代码应比生产代码更具可读性。 明确的测试应遵循以下基本的线性步骤:

  1. 创建被测对象。 在上面的代码中,这是在setUp()方法中完成的,因为我正在对所有(一个)测试使用相同的被测对象。
  2. 设置测试。 这是在测试方法testFindAddressWithStub()中完成的,因为测试中使用的数据特定于该测试。
  3. 运行测试
  4. 撕毁测试。 这样可以确保测试彼此隔离,并且可以按任何顺序运行。

使用简单的存根会产生将AddressService与外界隔离和快速运行测试的两个好处。

这种测试有多脆? 如果您的要求改变了,那么测试和存根就会改变–毕竟不是那么脆弱吗?

作为比较,我的下一个博客使用EasyMock重写了该测试。

参考: 定期的单元测试和存根-来自JCG合作伙伴 的Captain Debug博客上的 测试技术4

相关文章 :

  • 测试技巧–不编写测试
  • 端到端测试的滥用–测试技术2
  • 您应该对什么进行单元测试? –测试技术3
  • 使用模拟的单元测试–测试技术5
  • 为旧版代码创建存根–测试技术6
  • 有关为旧版代码创建存根的更多信息–测试技术7
  • 为什么要编写单元测试–测试技巧8
  • 一些定义–测试技术9
  • 使用FindBugs产生更少的错误代码
  • 在云中开发和测试

翻译自: https://www.javacodegeeks.com/2011/11/regular-unit-tests-and-stubs-testing.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/374230.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

中微CMS32 Keil环境搭建

打开中微官网https://www.mcu.com.cn/Products/113/pids/.html 把这三个资料都下载好。 环境搭建需要用的就是第三个pack包了 坑爹的是pack包下载下来是.zip格式 下载好后需要修改为.pack格式 运行即可。 打开三个资料中的demo code的工程 target和device都能识别出 编…

Windows下sass无法编译

最近windows下使用sass老是出现各种莫名其买的问题,下面是我的一些解决办法 windows下编译sass不支持中文解决办法网上给的是 解决办法: 1.koala可视化编译工具, 找到安装目录里面sass-3.3.7模块下面的engine.rb文件,例如下面路径…

数据库面试中常用的10个问题

1.触发器的作用?答:触发器是一中特殊的存储过程,主要是通过事件来触发而被执行的。它可以强化约束,来维护数据的完整性和一致性,可以跟踪数据库内的操作从而不允许未经许可的更新和变化。可以联级运算。如&…

测试技巧–不编写测试

对此没有太多疑问,测试代码的方式是一个有争议的问题。 不同的测试技术因各种原因(包括企业文化,经验和总体心理观点)而受到不同开发人员的青睐。 例如,您可能更喜欢编写经典的单元测试,通过检查返回值来单…

Ubuntu镜像下载地址

https://mirrors.aliyun.com/ubuntu-releases/ 用迅雷下载速度挺快的

算法—实现排列 A(n,m)

/* 实现排列A&#xff08;n,m&#xff09;*/ #include "stdio.h" int m,n,a[30]; long s0; int main() {int p(int k);printf("input n(n<10):"); scanf("%d",&n);printf("input m(<1m<n):"); scanf("%d",&…

oracle忘记用户密码

在cmd命令行下输入sqlplus / as sysdba alter user system identified by abc; 就可以将system用户的密码改成abc了。 alter user sys identified by abc; sys用户的密码也改成abc了。 然后你再登录sqlplus: 转载于:https://www.cnblogs.com/zzlp/p/4936109.html

python初体验-hello world答案_Python初体验_基础(一)

一&#xff1a;变量 变量的赋值&#xff1a; name “Meng” 上述代码声明了一个变量&#xff0c;变量名为name&#xff0c;变量name的值为&#xff1a;”Meng“ 变量定义&#xff1a; 一个在内存存数据的容器。 变量的意义&#xff1a; 保存程序执行的中间结果或状态&#xff…

Codeforces Round #365 (Div. 2) D. Mishka and Interesting sum (离线树状数组+前缀xor)

题目链接&#xff1a;http://codeforces.com/contest/703/problem/D 给你n个数&#xff0c;m次查询&#xff0c;每次查询问你l到r之间出现偶数次的数字xor和是多少。 我们可以先预处理前缀和Xor[i]&#xff0c;表示1~i的xor和。因为num^num0&#xff0c;所以Xor[r] ^ Xor[l - 1…

九齐NY8B072A单片机使用笔记(二)TIMER1/2/3定时器

先上代码 volatile unsigned long g_timer0_delay_conut 0;void main(void) {DISI(); //Disable all unmasked interruptsNy8b072a_Gpio_Init();//Ny8b072a_Timer1_Init();//Ny8b072a_Timer2_Init();Ny8b072a_Timer3_Init();ENI(); // Enable all unmasked interrupts whil…

新的Java缓存标准(javax.cache)

这篇文章探讨了新的Java缓存标准&#xff1a;javax.cache。 它如何适应Java生态系统 该标准由JSR107开发&#xff0c;作者是共同规范负责人。 JSR107包含在JSR342开发的Java EE 7中。 Java EE 7将于2012年底完成。但是与此同时&#xff0c;javax.cache将在Java SE 6和更高版本…

Eclipse搭建scala环境(解决“JDT weaving is currently disabled”问题)

随着Apache Spark&#xff0c;scala也成了必学的语言&#xff0c;下面讲一下Eclipse搭建scala开发环境。 网上有很多的教程&#xff0c;但是给的scala的地址下载的插件无法开发scala&#xff0c;会出现“JDT weaving is currently disabled”的问题,这是由于使用了错误的Scala地…

python如何输出结果_如何在python2.7中打印输出结果?

我正在存储一些数据&#xff0c;如温度&#xff0c;湿度和强度&#xff0c;这是我的Arduino输出和输入为我的python2.7&#xff0c;我正在绘制图表的数据。我也想将Arduino输出存储到文本文件中&#xff0c;但是我无法这样做&#xff1a; 这是我的python代码import serial impo…

python字符串连接的三种方法及其效率、适用场景详解

python字符串连接的方法&#xff0c;一般有以下三种:方法1&#xff1a;直接通过加号()操作符连接website& 39;python& 39;& 39;tab& 39;& 39; com& 39;方法2 python字符串连接的方法&#xff0c;一般有以下三种: 方法1&#xff1a;直接通过加号()操作符…

算法—递归实现 C(m,n)

/* 递归实现 C(m,n) */#include "stdio.h" int m,n,s,a[20];int main() {int c(int k);s0; a[0]0;scanf("%d%d",&m,&n);printf("\nC(%d,%d)%d\n",m,n,c(1));}//组合递归函数C(k) int c(int k) {int i,j;if(k<n){for(ia[k-1]1;i<m…

九齐51单片机使用注意事项:不要用float

在使用ADC计算电压值时用了float&#xff0c;NY8B072A堆栈直接炸了&#xff0c;用32机习惯了&#xff0c;一直想不通&#xff0c;查了手册才知道。 手册是&#xff1a;《NYC_NY8_UM_v1.5_SC.pdf》 链接&#xff1a;https://www.nyquest.com.tw/cn/support/download/Nyquest_SW…

有益的CountDownLatch和棘手的Java死锁

您是否曾经使用过java.util.concurrent.CountDownLatch &#xff1f; 这是在两个或多个线程之间实现同步的非常方便的类&#xff0c;在该类中&#xff0c;一个或多个线程可以等待&#xff0c;直到在其他线程中执行的一组操作完成为止&#xff08;请参阅javadoc和此文章 &#x…

算法—回溯法桥本分数式

/* 将1-9九个数不重复地赋给不同的9个元素 &#xff0c;实现形如a/bcd/eff/hi 的形式&#xff1a;例&#xff1a;1/265/784/39 1/325/967/84 &#xff08;注意&#xff1a;1/265/784/39 和5/781/264/39 只能算一种解&#xff09;求满足条件的解共有多少个&#xff1f; */ #in…

codeforces 703B

题意&#xff1a;有n座城市&#xff0c;其中k座是省会城市&#xff0c;每个城市有对应的点权&#xff0c;城市1-2-3-...-n-1有一条路相连&#xff0c;省会城市与其他所有的城市相连&#xff0c;且每两个城市间最多有一条路&#xff0c;每条路的边权为路连接的两座城市的点权乘积…

go 基准测试 找不到函数_基于Golang做测试

本文在实习期间完成并完善&#xff0c;无任何公司机密&#xff0c;仅做语言交流学习之用。持续更新。1.Golang的单元测试Go语言提供了丰富的单测功能。在Go中&#xff0c;我们通常认为函数是最小的可执行单元。本例中使用两个简单的函数&#xff1a;IsOdd和IsPalindrome来进行G…