软件测试技术之单元测试—工程师 Style 的测试方法

这篇具有很好参考价值的文章主要介绍了软件测试技术之单元测试—工程师 Style 的测试方法。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

什么是单元测试?

Wikipedia 对单元测试的定义:

在计算机编程中,单元测试(Unit Testing)又称为模块测试,是针对程序模块(软件设计的最小单位)来进行正确性检验的测试工作。

在实际测试中,一个单元可以小到一个方法,也可以大到包含多个类。从定义上讲,单元测试和集成测试是有严格的区分的,但是在实际开发中它们可能并没有那么严格的界限。如果专门追求单元测试必须测试最小的单元,反而容易造成多余的测试并且不易维护。换句更严谨一点的说法,我们要考虑测试的场景再去选择不同粒度的测试。

单元测试和集成测试即可以手工执行,也可以是程序自动执行。但现在一般提到单元测试,都是指自动执行的测试。所以我们下面提到的单元测试,没有特别注明,都是泛指自动执行的单元测试或集成测试。

单元测试入门

下面我们先看两个案例,感受一下单元测试到底是什么样子的。

例子 1:生命游戏单元测试

我们先看一个很简单的例子,实现一个康威生命游戏。如果不了解康威生命游戏的话,可以看 Wikipedia 的介绍。假设我们实现时定义这样的接口:

public interface Game {

void init(int[][] shape) ; // 初始化游戏 board

void tick(); // 行进到在一个回合

int[][] get(); // 获取当前游戏 board

}

生命游戏有好几条规则,为了测试我们的实现是否正确,我们可以针对生命游戏的每个规则,写一个单元测试。下面测试的是复活的规则。

@Test

public void testRelive() {

int[][] shape = {{0, 0, 1}, {0, 0, 0}, {1, 0, 1}};

Game g = new GameImplSample(shape);

g.tick();

// 自己死亡,周围3个存活状态,复活

assertEquals(1, g.get()[1][1]);

}

例子 2:订单退款集成测试

我们在看一个稍微复杂一些的例子,测试的是订单退款的过程。

@Test

public void test300_doRefundItem() {

// 创建订单、支付,然后退款

Order order = createOrder(OrderSource.XR_DOCTOR);

order = fullPay(order, PayType.WECHAT_JS);

OrderItem item = _doItemRefund(order, 1, false);

// 检查退款中状态

OrderWhole orderWholeRefunding = findOrderWhole(order.getOrderNo());

isTrue(orderWholeRefunding.getRefundStatus().equals(

OrderRefundStatus.PARTIAL_REFUNDING));

isTrue(orderWholeRefunding.getRefunds().get(0).getStatus().equals(

RefundStatus.REFUNDING));

isTrue(orderWholeRefunding.getRefunds().get(0).getItemId().get().equals(

item.getId()));

// 构建退款的回调信息

List payments = findPayments(order.getId());

List refunds = findRefunds(order.getId());

wxRefundNotify(payments.get(0), refunds.get(0), WxRefundStatus.SUCCESS);

// 检查退款后状态

OrderWhole orderWholeFinish = assertRefund(order, FULL_PAID,

PARTIAL_REFUND_OK, RefundStatus.SUCCESS, RefundMode.ITEM, false);

isTrue(orderWholeFinish.getRefundFee() == item.getPaidPrice());

isTrue(orderWholeFinish.getIncomes().stream()

.filter(i -> i.getAmount() < 0).count() == 1);

}

单元测试执行

单元测试有很多种执行方式:

在 IDE 中执行

通过 mvn 或者 gradle 运行

在 CI 中执行

不论什么方式,单元测试都应该很容易就能运行,并给出一个测试结果。当然,单元测试运行速度得快,一般是在秒级的,太慢的话就不能及时获得反馈了。

为什么要写单元测试?

单元测试的好处

确保代码满足需求或者设计规格。 使用单元测试来测试代码,可以通过构造数据和前置条件,确保测试覆盖到需要测试的逻辑。而手工测试或 UI 测试则无法做到,并且往往更复杂。

快速定位并解决问题。 单元测试因为测试范围比较小,可以比较容易的定位到问题;而手工测试,常常需要耗费不少时间去定位问题。

确保代码永远满足需求规格。 一旦需要对实现进行修改,单元测试可以确保代码的正确性,极大的降低各种修改和重构的风险。特别是避免那些在意想不到之处出现的 BUG。

简化系统集成。 单元测试确保了系统或模块本身的正确性,集成时更不容易出错。

提高代码质量和可维护性。 不可测试的代码,其本身的抽象性、模块性、可维护性是有些问题的。例如不符合单一职责、接口隔离等设计原则,或者依赖了全局变量。可测试的代码,往往其质量相对会高一些。

提供文档和说明。 单元测试本身就是接口使用方法的很好的案例。

持续集成和持续交付

2010 年前后,大部分互联网公司的系统部署还是通过手工的方式进行的,往往要在半夜上线系统。但是之后持续集成、持续交付的理念不断推广,部署过程越来越灵活、顺畅。而单元测试则是持续集成和持续交付里重要的一环。

持续集成就是 Continuous Integration(CI),也就是指从开发上传代码、自动构建和测试、最后反馈结果的过程。

更进一步,如果自动构建和测试后,会自动发布到测试环境或预发布环境,执行更多测试(集成测试、自动化 UI 测试等),甚至最后直接发布,那这一过程就是持续交付(Continuous Delivery,CD)。业内有不少公司,比如亚马逊、Esty,可以做到每天几十甚至成百上千次生产环境部署,就是因为有比较完善的持续交付环境。

CI 已经是互联网行业必备标准,CD 也在互联网行业有了越来越多的实践,但是如果没有单元测试这一环节,CI 和 CD 的过程是有缺陷的。

怎么写单元测试?

JUnit 简介

基本上每种语言和框架都有不错的单元测试框架和工具,例如 Java 的 JUnit、Scala 的 ScalaTest、Python 的 unittest、JavaScript 的 Jest 等。上面的例子都是基于 JUnit 的,我们下面就简单介绍下 JUnit。

JUnit 里面每个 @Test 注解的方法,就是一个测试。@Ignore 可以忽略一个测试。@Before、@BeforeClass、@After、@AfterClass 可以在测试执行前后插入一些通用的操作,比如初始化和资源释放等等。

除了 assertEquals,JUnit 也支持不少其他的 assert 方法。例如 assertNull、assertArrayEquals、assertThrows、assertTimeout 等。另外也可以用第三方的 assert 库比如 Spring 的 Assert 或者 AssertJ。

除了可以测试普通的代码逻辑,JUnit 也可以进行异常测试和时间测试。异常测试是测试某段代码必须抛指定的异常,时间测试则是测试代码执行时间在一定范围内。

也可以对测试进行分组。例如可以分成 contractTest 、mockTest 和 unitTest,通过参数指定执行某个分组的测试。

这里就不做过多介绍了,想了解更多 JUnit 的可以去看 极客学院的 JUnit 教程 等资料。其他的单元测试框架,基本功能都是大同小异。

使用测试 Double

狭义的单元测试,我们是只测试单元本身。即使我们写的是广义的单元测试,它依然可能依赖其他模块,比如其他类的方法、第三方服务调用或者数据库查询等等,造成我们无法很方便的测试被测系统或模块。这时我们就需要使用测试 Double 了。

如果细究的话,测试 Double 分成好多种,比如什么 Dummies、Fakes 等等。但我认为我们只要弄清两类就可以了,也就是 Stub 和 Mock。

Stub

Stub 指那些包含了预定义好的数据并且在测试时返回给调用者的对象。Stub 常被用于我们不希望返回真实数据或者造成其他副作用的场景。

我们契约测试生成的、可以通过 spring cloud stubrunner 运行的 Stub Jar 就是一个 Stub。我们可以让 Stub 返回预设好的假数据,然后在单元测试里就可以依赖这些数据,对代码进行测试。例如,我们可以让用户查询 Stub 根据参数里的用户 ID 返回认证用户和未认证用户,然后我们就可以测试调用方在这两种情况下的处理逻辑了。

当然,Stub 也可以不是远程服务,而是另外一个类。所以我们经常说要针对接口编程,因为这样我们就可以很容易的创建一个接口的 Stub 实现,从而替换具体的类。

public class StubNameService implement NameService {

public String get(String userId) {

return ““Mock user name””;

}

}

public class UserServiceTest {

// UserService 依赖 NameService,会调用其 get 方法

@Inject

private UserService userService;

@Test

public void whenUserIdIsProvided_thenRetrievedNameIsCorrect() {

userService.setNameService(new StubNameService());

String testName = userService.getUserName(““SomeId””);

Assert.assertEquals(““Mock user name””, testName);

}

}

不过这样要实现很多 Stub 也是很麻烦的,现在我们已经不需要自己创建 Stub 了,因为有了各种 Mock 工具。

Mock

Mocks 指那些可以记录它们的调用信息的对象,在测试断言中我们可以验证 Mocks 被进行了符合期望的调用。

Mock 和 Stub 的区别在于,Stub 只是提供一些数据,它并不进行验证,或者只是基于状态做一些验证;而 Mock 除了可以做 Stub 的事情,也可以基于调用行为进行验证。比如说,Mock 可以验证 Mock 接口被调用了不多不少正好两次,并且调用的参数是期望的数值。

Java 里最常用的 Mock 工具就是 Mockito 了。我们来看一个简单的例子,下面的 UserService 依赖 NameService。当我们测试 UserService 的时候,我们希望隔离 NameService,那么就可以创建一个 Mock 的 NameService 注入到 UserService 中(在 Spring 里只需要用 @Mock 和 @InjectMocks 两个注解就可以完成了)

public class UserServiceTest {

@InjectMocks

private UserService userService;

@Mock

private NameService nameService;

@Test

public void whenUserIdIsProvided_thenRetrievedNameIsCorrect() {

Mockito.when(nameService.getUserName(““SomeId””)).thenReturn(““Mock user name””);

String testName = userService.getUserName(““SomeId””);

Assert.assertEquals(““Mock user name””, testName);

Mockito.verify(nameService).getUserName(““SomeId””);

}

}

注意上面最后一行,是验证 nameService 的 getUserName 被调用,并且参数为 ““SomeId””。

契约测试

契约测试会给每个服务生成一个 Stub,可以用于调用方的单元/集成测试。例如,我们需要测试预约服务的预约操作,而预约操作会调用用户服务,去验证用户的一些基本信息,比如医生是否认证等。

所以,我们可以通过传入不同的用户 ID,让契约 Stub 返回不同状态的用户数据,从而验证不同的处理流程。例如,正常的预约流程的测试用例可能是这样的。

@RunWith(SpringJUnit4ClassRunner.class)

@SpringBootTest@AutoConfigureStubRunner(repositoryRoot=““http://<nexus_root>””,

ids = {"“com.xingren.service:user-client-stubs:1.0.0:stubs:6565"”})public class BookingTest {

// BookingService 会调用用户服务,获取医生认证状态后进行不同的处理

@Inject

private BookingService bookingService;

@Test

public void testBooking() {

BookingForm form = new BookingForm(

1,// doctorId

1// scheduleId

1001);// patientId

BookVO res = bookingService.book(form);

assertTrue(res.id > 0);

assertTrue(res.payStatus == PayStatus.UN_PAY);

}

}

注意上面的 AutoConfigureStubRunner 注解就是设置并启动了用户服务 Stub,当然在测试的时候,我们需要把服务调用接口的 baseUrl 设置为http://localhost:6565。关于契约测试的更多内容,请参考微服务环境下的集成测试探索一文。

TDD

简单说下 Test Driven Development,也就是 TDD。左耳朵耗子就写了一篇TDD并不是看上去的那么美,我就直接引用其介绍了。

其开发过程是从功能需求的test case开始,先添加一个test case,然后运行所有的test case看看有没有问题,再实现test case所要测试的功能,然后再运行test case,查看是否有case失败,然后重构代码,再重复以上步骤。

其实严格的 TDD 流程实用性并不高,左耳朵耗子本身也是持批判态度。但是对于接口定义比较明确的模块,先写单元测试再写实现代码还是有很大好处的。因为目标清晰,而且可以立刻得到反馈。

c

文章来源:网络 版权归原作者所有

上文内容不用于商业目的,如涉及知识产权问题,请权利人联系小编,我们将立即处理文章来源地址https://www.toymoban.com/news/detail-660045.html

到了这里,关于软件测试技术之单元测试—工程师 Style 的测试方法的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 软件测试开发工程师常用的测试工具详解

    1. 操作系统: Linux: vmware: 用于虚拟化环境,创建和管理虚拟机。 xshell、xftp、ssh: 提供对Linux服务器的远程访问和文件传输。 2. 数据库: MySQL: SQLyog、Navicat: 前端连接工具,简化MySQL数据库的管理和操作。 Oracle: PLSQL Developer、Navicat: 前端连接工具,用于Oracle数据库的开发和

    2024年02月02日
    浏览(72)
  • 2023年软件测试工程师,初级到高级进阶路线指南,测试之路...

    提到软件测试工程师时,很多人依然会联想到那些“点点点”并企图在“点点点”中找到缺陷的人,也就是大家常说的依照测试规范和测试案例来对软件进行测试,检查软件是不是有缺陷,判断软件是不是稳定。但这其实是一个很不好的观点。 近年来,随着各大互联网企业的

    2024年02月09日
    浏览(62)
  • 软件测试工程师面试如何回答测试工作有什么优势和劣势

    软件测试工程师面试的时候,会遇到很多很奇葩的问题,例如今天要讲的这个问题就是很奇葩:测试工作有什么优势和劣势? 我们做软件测试工作的,为了能够把软件中的明显的缺陷找出来,要读几十遍需求文档,跟开发和产品使劲的沟通,有时候还要拿着竞争对手的产品分

    2024年02月02日
    浏览(65)
  • 软件测试工程师postman使用基本操作方法

    本文详细介绍了如何使用Postman进行软件测试,包括管理测试用例集,发送请求,设置全局和环境变量,编写前置脚本和断言,进行数据关联,实现文件参数化,以及使用Newman命令执行Postman脚本。

    2024年02月04日
    浏览(85)
  • 测试开发人均年薪30w+?软件测试工程师如何进阶拿到高薪?

    掌握什么样的技能可以让软件测试工程师获得高薪?在回答这个问题前,我们先了解一下软件测试行业的现状: PS :这里有一套2022最新版的 软件测试 全套 自学教程 ,包含了以下内容,记得一定要下载: ☑ 215集-零基础到精通全套视频课程 ☑ [PPT+代码]-完整配套的教学课件

    2023年04月12日
    浏览(51)
  • 软件测试工程师面试如何描述自动化测试是怎么实现的?

    软件测试工程师面试的时候,但凡简历中有透露一点点自己会自动化测试的技能点的描述,都会被面试官问,那你结合你的测试项目说说自动化测试是怎么实现的?一到这里,很多网友,包括我的学生,也都一脸懵逼的样子。 有心放弃吧,但是看着那么高的薪资,还是很眼热

    2024年02月13日
    浏览(81)
  • 一个优质软件测试工程师简历的范文(一定要收藏)

     很多刚转行软件测试的小伙伴是不是不知道怎么写好一份优质的软件测试工程师的简历。今天呢,就给大家分享一下一个优质软件测试工程师简历的范文。记得收藏起来哦。 下面的案例:2-3年的软件测试工程的简历 姓    名:XXX    学历:本科     电    话:186-XXXX-8888

    2024年02月02日
    浏览(68)
  • 2023软件测试工程师必备技能?要卷,谁还不会了......

    软件测试岗位是怎样的? 大伙:测试?简单啊,没什么技术含量,无非就是看需求、看业务手册、看设计文档、然后点点功能是否实现,麻烦点的就是测试下部署安装是否出现兼容性问题等 web自动化测试:https://www.bilibili.com/video/BV1MS4y1W79K/ 没错,不可否认这是踏入软件测试

    2023年04月20日
    浏览(67)
  • 月薪过 3w 的 软件测试工程师 都是怎么做到的?

    对任何职业而言,薪资始终都会是众多追求的重要部分。前几年的软件测试行业还是一个风口,随着不断地转行人员以及毕业的大学生疯狂地涌入软件测试行业,目前软件测试行业“缺口”已经基本饱和。 当然,我说的是最基础的功能测试的岗位需求已经很少了,而自动化、

    2023年04月19日
    浏览(65)
  • 在职阿里8年,一个31岁女软件测试工程师的心声

    简单的先说一下,坐标杭州,13届本科毕业,算上年前在阿里巴巴的面试,一共有面试了有6家公司(因为不想请假,因此只是每个晚上去其他公司面试,所以面试的公司比较少) 其中成功的有4家,另外2家失败的原因在于: 1.对于系统知识的了解不够全面,在最后一轮主管面

    2024年02月07日
    浏览(47)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包