[Android]单元测试和模块测试

在 Kotlin 开发中,单元测试和模块测试(有时也称为组件测试或服务测试)是两种关键的测试方法,它们帮助开发者确保代码的各个部分独立和整体上都按预期工作。

1.单元测试

单元测试是测试软件应用中最小单元(通常是方法或函数)的过程。它的目标是验证这些单元在隔离的环境中的行为是否符合预期。

优点

  • 快速执行。
  • 帮助开发者识别和修复问题的精确位置。
  • 通过测试用例作为文档,可以增加代码的可读性和可维护性。

常用工具

  • JUnit: Java和Kotlin中最流行的测试框架。
  • MockK: 为Kotlin特别设计的模拟库,支持协程和扩展函数的模拟。
  • Spek: 使用Kotlin DSL的基于规格的测试框架。

示例 1: 简单的单元测试

假设我们有一个简单的 Calculator 类:

class Calculator {fun add(a: Int, b: Int): Int = a + bfun multiply(a: Int, b: Int): Int = a * b
}

为这个类编写单元测试:

import org.junit.jupiter.api.Test
import org.junit.jupiter.api.Assertions.assertEqualsclass CalculatorTest {private val calculator = Calculator()@Testfun `add should correctly add two numbers`() {assertEquals(5, calculator.add(2, 3), "Adding 2 and 3 should equal 5")}@Testfun `multiply should correctly multiply two numbers`() {assertEquals(6, calculator.multiply(2, 3), "Multiplying 2 by 3 should equal 6")}
}

示例 2: 使用 MockK 模拟依赖

假设我们有一个依赖外部服务的 UserService 类:

class UserService(private val userApi: UserApi) {fun getUserEmail(userId: Int): String? {val user = userApi.fetchUserById(userId)return user?.email}
}data class User(val id: Int, val name: String, val email: String)
interface UserApi {fun fetchUserById(userId: Int): User?
}

为 UserService 编写单元测试,使用 MockK 模拟外部 API:

import io.mockk.every
import io.mockk.mockk
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.Assertions.assertEqualsclass UserServiceTest {private val userApi = mockk<UserApi>()private val userService = UserService(userApi)@Testfun `getUserEmail returns correct email`() {val user = User(1, "John Doe", "john@example.com")every { userApi.fetchUserById(1) } returns userval email = userService.getUserEmail(1)assertEquals("john@example.com", email)}@Testfun `getUserEmail returns null if user not found`() {every { userApi.fetchUserById(any()) } returns nullval email = userService.getUserEmail(99)assertEquals(null, email)}
}

2.模块测试

模块测试或组件测试通常涉及一个模块或多个紧密相关的模块(如一个类及其依赖)。这种测试可能涉及数据库、文件系统、网络或其他外部依赖的模拟。

优点

  • 验证模块间的集成是否正确。
  • 检查模块与外部系统的交互。
  • 找到可能在单元测试中被忽略的问题。

常用工具

  • JUnit 或 TestNG: 用于编写和执行测试。
  • MockK: 模拟外部依赖。
  • Testcontainers: 提供真实的外部服务测试环境,如使用Docker容器的数据库。

示例: 模块测试

假设我们有一个 OrderService 类,它依赖于 OrderRepository 和 PaymentService

class OrderService(private val orderRepository: OrderRepository, private val paymentService: PaymentService) {fun processOrder(orderId: Int, paymentDetails: PaymentDetails): Boolean {val order = orderRepository.findById(orderId) ?: return falsereturn paymentService.processPayment(order, paymentDetails)}
}interface OrderRepository {fun findById(orderId: Int): Order?
}interface PaymentService {fun processPayment(order: Order, paymentDetails: PaymentDetails): Boolean
}data class Order(val id: Int, val amount: Double)
data class PaymentDetails(val method: String, val amount: Double)

为 OrderService 编写模块测试:

import io.mockk.every
import io.mockk.mockk
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Assertions.assertFalseclass OrderServiceTest {private val orderRepository = mockk<OrderRepository>()private val paymentService = mockk<PaymentService>()private val orderService = OrderService(orderRepository, paymentService)@Testfun `processOrder returns true when payment is successful`() {val order = Order(1, 100.0)val paymentDetails = PaymentDetails("credit",100.0)every { orderRepository.findById(1) } returns orderevery { paymentService.processPayment(order, paymentDetails) } returns trueval result = orderService.processOrder(1, paymentDetails)assertTrue(result, "Order processing should succeed when payment is successful")}@Testfun `processOrder returns false when order not found`() {every { orderRepository.findById(any()) } returns nullval result = orderService.processOrder(99, PaymentDetails("credit", 50.0))assertFalse(result, "Order processing should fail when order is not found")}@Testfun `processOrder returns false when payment fails`() {val order = Order(1, 100.0)val paymentDetails = PaymentDetails("debit", 100.0)every { orderRepository.findById(1) } returns orderevery { paymentService.processPayment(order, paymentDetails) } returns falseval result = orderService.processOrder(1, paymentDetails)assertFalse(result, "Order processing should fail when payment fails")}
}

3.断言方法

在 Kotlin 开发中,单元测试和模块测试通常使用测试框架,如 JUnit,Kotlin Test,或 Spek 等。这些框架提供了一系列的断言方法,这些方法是用来验证代码的行为是否符合预期的关键组件。我将重点介绍 JUnit 和 Kotlin Test 中常用的断言方法。

(1).JUnit 断言方法

JUnit 是 Java 和 Kotlin 中最常用的测试框架之一。JUnit 5 提供了一个 Assertions 类,其中包含多种静态方法用于断言测试结果。

基本断言
  • assertEquals(expected, actual, message): 验证两个值是否相等。
  • assertNotEquals(unexpected, actual, message): 验证两个值是否不等。
  • assertTrue(condition, message): 验证条件是否为真。
  • assertFalse(condition, message): 验证条件是否为假。
  • assertNull(value, message): 验证对象是否为 null
  • assertNotNull(value, message): 验证对象是否非 null
对象和类断言
  • assertSame(expected, actual, message): 验证两个引用是否指向同一个对象。
  • assertNotSame(unexpected, actual, message): 验证两个引用是否不指向同一个对象。
数组和集合断言
  • assertArrayEquals(expected, actual, message): 验证两个数组是否相等。
异常断言
  • assertThrows(exceptionType, executable, message): 验证预期的异常是否被抛出。
组合断言
  • assertAll(executables): 同时执行多个断言,只有当所有断言都通过时,测试才会通过。
示例
import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.Testclass AssertionsExample {@Testfun testAssertions() {assertEquals(4, 2 + 2, "Addition should work")assertTrue(5 > 2, "5 is greater than 2")assertAll("multiple",{ assertNotNull("Hello", "Should not be null") },{ assertThrows(ArithmeticException::class.java, { val result = 2 / 0 }, "Should throw ArithmeticException") })}
}

(2).Kotlin Test 断言方法

Kotlin Test 是另一种专为 Kotlin 设计的测试库,它提供了一些额外的断言功能。

基本断言
  • shouldBe: 用于任何类型,比较是否相等。
  • shouldNotBe: 用于任何类型,比较是否不等。
  • shouldBeNull: 检查对象是否为 null
  • shouldNotBeNull: 检查对象是否非 null
  • shouldThrow: 检查是否抛出异常。
集合断言
  • shouldContain: 检查集合是否包含元素。
  • shouldNotContain: 检查集合是否不包含元素。
字符串断言
  • shouldStartWith: 检查字符串是否以特定前缀开始。
  • shouldEndWith: 检查字符串是否以特定后缀结束。
示例
import io.kotest.matchers.shouldBe
import io.kotest.matchers.nulls.shouldBeNull
import io.kotest.matchers.string.startWith
import io.kotest.core.spec.style.StringSpecclass KotlinTestExample : StringSpec({"addition works" {(2 + 2) shouldBe 4}"null checks" {val str: String? = nullstr.shouldBeNull()}"string should start with" {"hello world".shouldStartWith("hello")}
})

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

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

相关文章

算法分析 KMP算法中next值的计算、0/1背包问题

5.6.1 KMP算法中next值的计算 设模式的长度为m。用蛮力法求解 KMP算法中的 next值时&#xff0c;next[0]可直接给出&#xff0c;计算next[j](1<j<m-1)则需要在 T[0] …T[j-1]中分别取长度为j-1、..、2、1的真前缀和真后缀并比较是否相等&#xff0c;最坏情况下的时间代价…

解析Linux键盘组合键产生信号的完整过程:从硬件中断到信号发送

前言 每一个了解Linux的都知道这样一个知识&#xff0c;CtrlC组合键能够终止一个进程。 个人了解进程相关知识之后知道&#xff0c;一个进程被终止只会有有三种情况&#xff1a; 代码运行完毕&#xff0c;结果正确代码运行完毕&#xff0c;结果不正确代码运行异常&#xff…

鸿蒙OpenHarmony【基于Hi3516DV300开发板(时钟应用开发)】

概述 本文将介绍如何快速搭建基于OpenHarmony标准系统&#xff08;Hi3516DV300开发板&#xff09;的应用开发环境&#xff0c;并基于一个时钟APP示例逐步展示应用的创建、开发、调试和安装等流程。示例代码可以通过本链接获取。 时钟App是一款显示实时时间的应用&#xff0c;…

【GDPU】数据结构实验十 哈夫曼编码

【实验内容】 1、假设用于通信的电文仅由8个字母 {a, b, c, d, e, f, g, h} 构成&#xff0c;它们在电文中出现的概率分别为{ 0.07, 0.19, 0.02, 0.06, 0.32, 0.03, 0.21, 0.10 }&#xff0c;试为这8个字母设计哈夫曼编码。 提示:包含两个过程:&#xff08;1&#xff09;构建…

【iOS】——浅析CALayer

文章目录 一、CALayer介绍二、UIview与CALayer1.区别2.联系 三、CALayer的使用1.初始化方法2.常用属性 四.CALayer坐标系1.position属性和anchorPoint属性2.position和anchorPoint的关系3.position、anchorPoint和frame的关系 五、CALayerDelegate六、CALayer绘图机制1.绘图流程…

利用Jenkins完成Android项目打包

问题和思路 目前存在的问题 打包操作由开发人员完成&#xff0c;这样开发进度容易被打断。 解决问题的思路 将打包操作交测试/产品/开发人员来完成&#xff0c;主要是测试/开发。 按照以上的思路&#xff0c;那么JenkinsGradle的解决方案是比较经济的&#xff0c;实现起来…

在什么情况下表单会被重复提交?如何避免?

表单被重复提交是Web应用中常见的问题&#xff0c;通常在用户提交表单后点击按钮多次&#xff0c;或在表单提交后刷新页面时发生。这可能导致数据的重复处理&#xff0c;比如重复记录或订单。 何时会发生表单重复提交&#xff1f; 用户多次点击提交按钮&#xff1a;在网络延迟…

鸿蒙内核源码分析(互斥锁篇) | 互斥锁比自旋锁丰满多了

内核中哪些地方会用到互斥锁?看图: 图中是内核有关模块对互斥锁初始化,有文件,有内存,用消息队列等等,使用面非常的广.其实在给内核源码加注的过程中,会看到大量的自旋锁和互斥锁,它们的存在有序的保证了内核和应用程序的正常运行.是非常基础和重要的功能. 概述 自旋锁 和…

5.7 线程

进程&#xff1a;解耦稳定&#xff0c;内容之间是不相关的&#xff0c;通信不便利&#xff0c;理论上进程的软硬件的切换时间以及创建开销非常大。--------》资源共享线程实现 线程的问题&#xff1a;本质就是不解耦&#xff0c;一个出问题别的就很有可能出问题&#xff0c;同…

Scanner中next()、nextInt()、nextLine()、hasNext()、hasNextInt()的使用方法及注意事项

目录 1、next()、nextInt()、nextLine()的使用方法及区分 2、循环时如何使用hasNext方法 3、用hasNextInt()作为判断下一个输入是否为数字需要配合next()方法使用 1、next()、nextInt()、nextLine()的使用方法及区分 三者简单定义 next()&#xff1a;此方法遇见第一个有效字符…

使用AIGC生成软件类图表

文章目录 如何使用 AI 生成软件类图表什么是 MermaidMermaid 的图片如何保存&#xff1f;mermaid.liveDraw.io Mermaid可以画什么图&#xff1f;流程图时序图 / 序列图类图状态图甘特图实体关系图 / ER图 如何使用 AI 生成软件类图表 ChatGPT 大语言模型不能直接生成各类图表。…

linux系统下产生Segmentation fault 与 Segmentation fault (core dumped)!!!

最近在学习的过程中&#xff0c;遇到了Segment fault&#xff08;段错误&#xff09;的问题&#xff0c;经过一番查找资料&#xff0c;学到了一些相关知识&#xff0c;这里做一个梳理&#xff0c;以防以后在遇到类似的问题&#xff0c;并且希望能够帮助到大家一丝丝&#xff01…

tensorflow学习笔记(2)线性回归-20240507

通过调用Tensorflow计算梯度下降的函数tf.train.GradientDescentOptimizer来实现优化。 代码如下: #!/usr/bin/env python3 # -*- coding: utf-8 -*- #程序作用: #线性回归:通过调用Tensorflow计算梯度下降的函数tr.train.GradientDescentOptimizer来实现优化。import os …

python中numpy库使用

array数组 生成array数组 将list转化为array数组 import numpy as np np.array([1,2],typenp.int32)其中dtype定义的是元素类型&#xff0c;np.int32指32位的整形 如果直接定义dtypeint 默认的是32位整形。 zeors和ones方法 zeros()方法&#xff0c;该方法和ones()类似&a…

有什么方便实用的成人口语外教软件?6个软件教你快速进行口语练习

有什么方便实用的成人口语外教软件&#xff1f;6个软件教你快速进行口语练习 口语能力在语言学习中占据着重要的位置&#xff0c;因为它直接关系到我们与他人进行交流和沟通的效果。为了提高口语能力&#xff0c;很多成人选择通过外教软件进行口语练习&#xff0c;这些软件提供…

php之框架底层中间件模式开发实现、array_reduce的应用

众所周知php框架的中间件核心是通过array_reduce实现的 php之框架中间件模式开发实现、array_reduce的应用 1.先写个测试用例看一下函数的特性2.根据执行特性实现中间件 1.先写个测试用例看一下函数的特性 <?phpfunction kernal($a,$b){return $a . " and " .…

GNU Radio FFT模块结合stream to vector应用及Rotator频偏模块使用

文章目录 前言一、FFT 模块应用1、stream to vector 介绍2、创建 grc 图测试3、运行结果 二、频偏模块1、Rotator 简介2、创建 grc 图测试3、运行结果 前言 写个博客记录一下自己的蠢劲儿&#xff0c;之前我想用 FFT 模块做一些信号分析的东西&#xff0c;官方的 FFT 模块必须…

营销5.0时代,企业的痛如何解?

进入营销5.0阶段之后&#xff0c;许多企业都需解决连接客户效能低下的问题。针对这个问题&#xff0c;产品经理、软件开发公司包括个人开发者&#xff0c;要怎么找到有效的“解药”&#xff1f; 营销不仅每年都在变化&#xff0c;甚至每天都在变化。 ——现代营销学之父&…

【再探】设计模式—适配器、装饰及外观模式

结构型设计模式是用于设计对象和类之间关系的一组设计模式。一共有7种&#xff1a;适配器模式、装饰器模式、外观模式、桥接模式、组合模式、享元模式及代理模式。 1 适配器模式 需求&#xff1a;在软件维护阶段&#xff0c;已存在的方法与目标接口不匹配&#xff0c;需要个中…

论文阅读:RHO-1:Not All Tokens Are What You Need 选择你需要的 Tokens 参与训练

论文链接&#xff1a;https://arxiv.org/abs/2404.07965 以往的语言模型预训练方法对所有训练 token 统一采用 next-token 预测损失。作者认为“并非语料库中的所有 token 对语言模型训练都同样重要”&#xff0c;这是对这一规范的挑战。作者的初步分析深入研究了语言模型的 t…