开发必备-----单元测试

简介: 你好看官,里面请!今天笔者讲的是单元测试。不懂或者觉得我写的有问题可以在评论区留言,我看到会及时回复。 注意:本文仅用于学习参考,不可用于商业用途,如需转载请跟我联系。

单元测试

如果觉得写的还可以,点个赞支持一下笔者呗!你的点赞和关注会让我更快更新哦。笔者会持续更新关于Java和大数据有关的文章。目前集中精力在更新java框架的内容。

1. 为什么要写单元测试

了解软件工程的人都知道,测试是软件开发阶段的最后一环,是保证软件质量的关键步骤(这也是为什么测试人员会被称作 QA 的原因)。只有通过严谨的测试,确保软件达到预期,才能够交付/上线。而单元测试是软件测试中与开发人员关系最紧密的一个环节。

刚刚接触单元测试的时候,看到它的名字我脑海中不由自主的蹦出一个疑问——测试不应该是专门的测试人员做的事情吗,跟开发人员有毛线关系呢?你是不是和我一样有过同样的疑问呢?后来随着对单元测试有了更深入的了解以后,发现写单元测试是每个开发者责无旁贷的事情。

曾经看到过一篇文章写单元测试的七种境界,可以看看自己在哪一层。七种境界大致如下:

  1. 以各种借口拒绝单元测试
  2. 尝试单元测试
  3. 单元测试一切
  4. 无法忍受脆弱的单元测试
  5. 发现了一种模拟 mocking 框架,并且乐于使用强制语义
  6. 模拟 mock 所有可能模拟 mocked 的对象
  7. 开始真正有效单元测试

通常来说,开发人员对代码进行一次改动,就应该进行一次单元测试。在编写完一个相对独立和完整的功能时,就应该为其编写对应的单元测试代码,而不是等功能开发完再去写单元测试,

不过在我有限的了解中,能够做到单元测试与业务代码同步开发的,真的是凤毛麟角了。能够在写完业务代码就去完成单元测试已经是不错的情况了,更为甚者等功能都上线了,再回过头来去补写单元测试。这种为了写单元测试而写单元测试的做法,完全背离的单元测试的初衷。不过这还不是最糟糕的,还有完全不写单元测试的呢。

单元测试理想的情况是,语句覆盖率可以达到 70% ;核心模块的语句覆盖率和分支覆盖率都要达到 100% 的标准(当年在一家日企工作时,就是这个标准)。

单元测试的意义

  • 对自己代码质量的一种保障
  • 为将来的重构保驾护航
  • 可以让新人更快熟悉代码

测试金字塔

还记得我们之前讲过的学习金字塔吗?今天我们聊一下另外一个金字塔——测试金字塔:

网络异常,图片无法展示
|

测试金字塔大致可以分为三层,由下到上分别是:

  • 单元测试
  • 服务测试
  • 用户界面测试

测试金字塔要表达的思想是:自下而上,成本越来越高(右边箭头),效率越来越低(左边箭头)。说得通俗一点就是,往上走事倍功半,往下走事半功倍。有一种说法,这三层的相对收益比(由上到下)为:1 : 2 : 7。但是目前很多公司还在 UI 层面拼命努力着。

结合我们之前讲过的学习金字塔,我们会发现一个非常有意思的现象——大多数人都在收益率最低的那一层拼命努力!想到这里,你有没有觉得后背发凉呢?有没有意识到自己曾经或者正在在某个低收益的层面拼命努力着呢?

2. 一个单元测试的自我修养

身为一个单元测试,要明确自己的定位,时时刻刻都要谨记——自己是一个单元测试。那么我们来看一下具备哪些素质才能称之为一个合格的单元测试呢?

  • 有明确的预期
  • 可重复运行
  • 独立且完整

有明确的预期,这个很好理解,我们的程序输入什么,将会输出什么,这是对于软件的最低要求;

可重复运行也不必多说了,如果我们的单元测试都是一锤子买卖,那也就没有什么写的必要了;

作为一个人,我们要有独立完整的自尊体系,同样,作为一个单元测试也要做到独立且完整。怎么理解呢?

举个例子,假如我们要写一个 Service 层的单元测试,那么运行这个单元测试的时候,就不能去真的访问数据库(因为与数据库打交道是 Dao 层的工作),这叫做独立。虽然不能访问数据库,但是还要保证整个流程可以正确完整的走通(你这不是强人所难吗?),这就是要完整。

那么我们如何做到独立且完整呢?答案就是—— Mock。市面上有很多的 Mock 框架,比如 Mockito、Jmock、easyMock 等。借助这些工具我们可以很轻松的 Mock 出我们想要的依赖。

独立且完整是单元测试的核心思想所在,单元二字将这种思想表达的淋漓尽致。在做单元测试时,我们默认当前功能的所有依赖都是可以正确执行的,我们的目的是测试当前功能的正确性,而这些 Mock 框架可以很好的将被测功能与其他功能隔离开。

3. JUnit

JUnit 是一个 Java 语言的单元测试框架,也是 Spring Boot 默认的单元测试工具,我们先来看一下 JUnit 的几个核心概念和常用注解。

JUnit 核心概念:

JUnit 的相关概念 说明
Test Class (测试类) 一个测试类包含一个或多个测试用例
Test Case (测试用例) 一个以 @Test 注释的方法称为 一个测试用例,测试用例必须存在于测试类中。
Assert
(断言)
定义想测试的条件,当条件成立时, Assert 方法保持沉默,条件不成立时则抛出异常
Suite
(测试组)
关联性紧密的一组测试可以定义为一个 Suite
Runner
(运行器)
单元测试的运行器(如 SpringRunner),JUnit4 是向后兼容的,可以运行 JUnit3 的测试

JUnit 常用注解:

注解 说明
@RunWith 指定运行器
@BeforeClass 作用于测试类类加载之前(静态方法)
@AfterClass 作用于测试类运行结束时(静态方法)
@Before 作用于测试用例运行前
@After 作用于测试用例运行后
@Test 作用于测试用例

4. 实战

OK,上面唠叨了那么多,是不是已经手痒了?接下来就来实践一把,过过瘾。要使用单元测试,我们首先需要添加依赖。不过,这次 Spring Boot 好像察觉到了我们的心思,在创建 Web 工程的时候,已经帮我们把 Test 的依赖添加好了,可见 Spring Boot 也是希望我们去写单元测试的。依赖引用如下:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-test</artifactId>
  <scope>test</scope>
</dependency>

HelloController 类中按 Command + N (WIn 下是 Alt + Insert)或者鼠标右键单击选择 Generate.. 然后在弹出的菜单中选择 Test...

网络异常,图片无法展示
|

然后在接下来的对话框按如下图所示,勾选上对于的复选框,然后点击 OK :

网络异常,图片无法展示
|

然后去 src/test 目录下去找到我们创建的测试类,然后补充剩下的代码:

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class HelloControllerTest {
    @Autowired
    private MockMvc mvc;
    @BeforeClass
    public static void beforeClass() {
        System.out.println("===before class===");
    }
    @Before
    public void setUp() throws Exception {
        System.out.println("===before method===");
    }
    @Test
    public void hello() throws Exception {
        MvcResult result = mvc.perform(get("/")
                                .param("name", "IMOOC")
                                .contentType(MediaType.APPLICATION_JSON))
                                    .andExpect(content()
                                    .string("Hello IMOOC"))
                                      .andReturn();
        System.out.println("==="+result.getResponse().getContentAsString()+"===");
    }
    @After
    public void tearDown() throws Exception {
        System.out.println("===after method===");
    }
    @AfterClass
    public static void afterClass() {
        System.out.println("===after class===");
    }
}

测试通过的运行效果:

...........
===before class===
01:16:18.853 [main] DEBUG org.springframework.t
test class [com.imooc.springboot.HelloControlle
...........
  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.1.5.RELEASE)
2019-06-17 01:16:19.602  INFO 66981 --- [      
...........
3.474 seconds (JVM running for 4.558)
===before method===
===Hello IMOOC===
===after method===
===after class===
...........

以上打印的内容进一步验证了上面几个注解的作用。

5. 总结

OK,我们一起学习了一下单元测试相关的内容。知道了 JUnit 的核心概念以及常用注解,并且做了一个 Spring Boot + JUnit 的实例,进一步加深了对于单元测试以及 JUnit 的理解。

相关文章
|
17天前
|
消息中间件 Java 数据库
【消息队列开发】 实现 VirtualHostTests 类——测试虚拟主机操作
【消息队列开发】 实现 VirtualHostTests 类——测试虚拟主机操作
|
17天前
|
消息中间件 存储 测试技术
【消息队列开发】 实现MemoryDataCenterTests类——测试管理内存数据
【消息队列开发】 实现MemoryDataCenterTests类——测试管理内存数据
|
1天前
|
JSON Java 测试技术
SpringBoot实用开发篇第二章(测试操作)
SpringBoot实用开发篇第二章(测试操作)
|
16天前
|
测试技术 程序员 开发者
软件测试项目式学习一(认识软件生命周期与开发模型及软件质量)
软件测试项目式学习一(认识软件生命周期与开发模型及软件质量)
10 0
|
17天前
|
敏捷开发 测试技术
【软件测试】 开发模型和测试模型
【软件测试】 开发模型和测试模型
|
17天前
|
消息中间件 API
【消息队列开发】 实现 MqClientTests 类——测试客户端
【消息队列开发】 实现 MqClientTests 类——测试客户端
|
17天前
|
消息中间件 Java 测试技术
【消息队列开发】 测试MessageFileManager(对硬盘中的消息操作)类
【消息队列开发】 测试MessageFileManager(对硬盘中的消息操作)类
|
17天前
|
消息中间件 测试技术 Linux
【消息队列开发】实现DataBaseManagerTests(测试单元)
【消息队列开发】实现DataBaseManagerTests(测试单元)
|
23天前
|
人工智能 算法 测试技术
探索软件自动化测试的未来:AI驱动的测试策略构建高效可靠的微服务架构:后端开发的新范式
【5月更文挑战第28天】 在软件开发的世界中,测试是确保产品质量的关键步骤。随着技术的进步和项目复杂性的增加,传统的手动测试方法逐渐显得力不从心。本文旨在探讨自动化测试的最新趋势——人工智能(AI)驱动的测试策略。我们将分析AI如何通过智能化的测试用例生成、测试执行优化以及结果分析来提高测试效率和精确性。文章还将讨论实施AI测试策略的挑战与机遇,为软件测试工程师提供未来技术转型的视角。 【5月更文挑战第28天】 在当今软件开发的快速迭代和复杂多变的环境中,传统的单体应用架构已经难以满足业务敏捷性和可扩展性的需求。微服务架构作为一种新的解决方案,以其服务的细粒度、独立部署和弹性伸缩等特性,正逐
|
1月前
|
内存技术
(3)(3.4) 使用测试版和开发版
(3)(3.4) 使用测试版和开发版
19 2