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

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

1.单元测试

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

优点

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

常用工具

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

示例 1: 简单的单元测试

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

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

为这个类编写单元测试:

import org.junit.jupiter.api.Test
import org.junit.jupiter.api.Assertions.assertEquals

class CalculatorTest {
    private val calculator = Calculator()

    @Test
    fun `add should correctly add two numbers`() {
        assertEquals(5, calculator.add(2, 3), "Adding 2 and 3 should equal 5")
    }

    @Test
    fun `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.assertEquals

class UserServiceTest {
    private val userApi = mockk<UserApi>()
    private val userService = UserService(userApi)

    @Test
    fun `getUserEmail returns correct email`() {
        val user = User(1, "John Doe", "john@example.com")
        every { userApi.fetchUserById(1) } returns user

        val email = userService.getUserEmail(1)

        assertEquals("john@example.com", email)
    }

    @Test
    fun `getUserEmail returns null if user not found`() {
        every { userApi.fetchUserById(any()) } returns null

        val 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 false
        return 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.assertFalse

class OrderServiceTest {
    private val orderRepository = mockk<OrderRepository>()
    private val paymentService = mockk<PaymentService>()
    private val orderService = OrderService(orderRepository, paymentService)

    @Test
    fun `processOrder returns true when payment is successful`() {
        val order = Order(1, 100.0)
        val paymentDetails = PaymentDetails("credit",100.0)
        every { orderRepository.findById(1) } returns order
        every { paymentService.processPayment(order, paymentDetails) } returns true

        val result = orderService.processOrder(1, paymentDetails)

        assertTrue(result, "Order processing should succeed when payment is successful")
    }

    @Test
    fun `processOrder returns false when order not found`() {
        every { orderRepository.findById(any()) } returns null

        val result = orderService.processOrder(99, PaymentDetails("credit", 50.0))

        assertFalse(result, "Order processing should fail when order is not found")
    }

    @Test
    fun `processOrder returns false when payment fails`() {
        val order = Order(1, 100.0)
        val paymentDetails = PaymentDetails("debit", 100.0)
        every { orderRepository.findById(1) } returns order
        every { paymentService.processPayment(order, paymentDetails) } returns false

        val 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.Test

class AssertionsExample {
    @Test
    fun 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.StringSpec

class KotlinTestExample : StringSpec({
    "addition works" {
        (2 + 2) shouldBe 4
    }
    "null checks" {
        val str: String? = null
        str.shouldBeNull()
    }
    "string should start with" {
        "hello world".shouldStartWith("hello")
    }
})

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

IP规划案例

整个OSPF环境IP基于172.16.0.0/16划分 172.16.0.0/16 先分成2个网段&#xff08;OSPF RIP&#xff09;&#xff0c;借1位172.16.0.0/17 ---OSPF 再按区域划分&#xff08;5个区域&#xff09;&#xff0c;借3位 172.16.0.0/20 ---Area 0 三个环回 MGRE 4个网…

京东工业优选商品详情API接口:解锁高效工业采购新体验

京东工业优选的商品详情API接口&#xff0c;允许开发者通过程序化的方式&#xff0c;快速获取平台上的商品详细信息。这些详细信息包括但不限于商品名称、价格、规格、库存、图片、评价等&#xff0c;为企业提供全方位的商品信息查询服务。 二、API接口的主要功能 实时查询&a…

练习项目后端代码解析注解篇(annotation)

前言 本来想从接口处入手的&#xff0c;但是一下看到接口里几十个方法&#xff0c;眼睛有点抗拒&#xff0c;想想还是先看作者写的自定义注解吧。 项目里有三个自定义注解&#xff1a; 分别是AccessLimit注解、OperationLogger注解、VisitLogger注解 AccessLimit注解 这是一…

Python爬虫教程:入门爬取网页数据

1.遵守法律法规 爬虫在获取网页数据时&#xff0c;需要遵守以下几点&#xff0c;以确保不违反法律法规&#xff1a; 不得侵犯网站的知识产权&#xff1a;爬虫不得未经授权&#xff0c;获取和复制网站的内容&#xff0c;这包括文本、图片、音频、视频等。 不得违反网站的使用条…

PXE远程部署CentOS系统

文章目录 在局域网内搭建PXE服务器PXE 启动组件PXE的优点实验一、搭建PXE服务器&#xff0c;实现远程部署CentOS系统环境准备server关闭防火墙安装组件准备 Linux 内核、初始化镜像文件及PXE引导文件配置启用TFTP 服务配置启动DHCP服务准备CentOS 7 安装源配置启动菜单文件 Cli…

【C 数据结构-动态内存管理】3. 伙伴系统管理动态内存

文章目录 【 1. 伙伴系统的结构设计 】【 2. 分配算法 】【 3. 回收算法 】 伙伴系统 本身是一种动态管理内存的方法&#xff0c;和边界标识法的区别是&#xff1a;使用伙伴系统管理的存储空间&#xff0c;无论是空闲块还是占用块&#xff0c;大小都是 2 的 n 次幂&#xff08;…

AI换脸免费软件Rope中文汉化蓝宝石版本全新UI界面,修复部分已知错误【附下载地址与详细使用教程】

rope蓝宝石版&#xff1a;点击下载 注意&#xff1a;此版本支持N卡、A卡、CPU&#xff0c;且建议使用中高端显卡&#xff0c;系统要求win10及以上。 Rope-蓝宝石 更新内容&#xff1a; 0214版更新&#xff1a; ①&#xff08;已修复&#xff09;恢复到以前的模型荷载参数。有…

GDPU 天码行空11

&#xff08;一&#xff09;实验目的 1、掌握JAVA中IO中各种类及其构造方法&#xff1b; 2、重点掌握IO中类所具有的IO操作方法&#xff1b; 3、熟悉软件中登录模块的开发方法&#xff1b; 4、掌握IO中读写常用方法。 5、进一步熟悉正则规则的使用方法。 &#xff08;二&…

初期Linux

一&#xff0c;系统分为 1.1window系统 个人 &#xff1a;win7&#xff0c;win8&#xff0c;Win10&#xff0c;Win11服务器版&#xff1a;window server 2003&#xff0c;window server 2008 1.2Linux系统 centos7redhatubantukali 1.3什么是Linux&#xff1f; Linux是基…

强一致性的皇冠:分布式事务模型的至高法则揭秘

关注微信公众号 “程序员小胖” 每日技术干货&#xff0c;第一时间送达&#xff01; 引言 分布式事务模型是分布式系统设计的核心&#xff0c;关键在于保证数据一致性和事务完整性&#xff0c;尤其强调强一致性。诸如2PC、3PC、Saga、TCC等模型与协议&#xff0c;应运而生以解…

Unity初级---初识生命周期

1. Awake() &#xff1a;唤醒函数&#xff0c;最先执行的函数&#xff0c;只执行一次&#xff0c;当脚本文件挂载的对象被激活时调用 2. OnEnable() &#xff0c;OnDisable()&#xff1a;当脚本启用和禁用时触发&#xff0c;可执行多次&#xff0c;触发的前提是脚本挂载的对象…

QT开发(四) 制作一个JSON检查小工具

1、JSON概念 1.1 定义 JSON&#xff08;JavaScript Object Notation&#xff09;是一种轻量级的数据交换格式&#xff0c;它易于人类阅读和编写&#xff0c;同时也易于机器解析和生成。JSON基于JavaScript语言的子集&#xff0c;但是独立于编程语言&#xff0c;因此可以被多种…

C语言----汉诺塔问题

1.什么是汉诺塔问题 简单来说&#xff0c;就是有三个柱子&#xff0c;分别为A柱&#xff0c;B柱&#xff0c;C柱。其中A柱从上往下存放着从小到大的圆盘&#xff0c;我们需要借助B柱和C柱&#xff0c;将A柱上的所有圆盘转移到C柱上&#xff0c;并且一次只能移动一个圆盘&#…

【Qt QML】Qt Quick Scene Graph

Qt Quick 2是一个用于创建图形界面的库&#xff0c;它使用一个专门的场景图&#xff08;Scene Graph&#xff09;来进行渲染。通过使用OpenGL ES、OpenGL、Vulkan、Metal或Direct 3D等图形API&#xff0c;Qt Quick 2可以有效地优化图形渲染过程。使用场景图而不是传统的命令式绘…

1688工厂货源API接口:用于商品采集、商品搜索、商品详情数据抓取

item_get 获得1688商品详情item_search 按关键字搜索商品item_search_img 按图搜索1688商品&#xff08;拍立淘&#xff09;item_search_suggest 获得搜索词推荐item_fee 获得商品快递费用seller_info 获得店铺详情item_search_shop 获得店铺的所有商品item_password 获得淘口令…

在拥有多个同名称密码的ap环境中,如何连接到指定信道或mac的ap路由器?

在给客户做ESP32-C3入墙开关项目时&#xff0c;客户问&#xff1a;在拥有多个同名称密码的ap环境中&#xff0c;如何连接到指定信道或mac的ap路由器&#xff1f;针对这个问题&#xff0c;启明云端工程师给出下面解决方法。 1、将wifi_sta_config_t配置中的channel配置为该信道…

神经网络怎么把隐含层变量融合到损失函数中?

&#x1f3c6;本文收录于「Bug调优」专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收藏&&…

matplotlib和pandas与numpy

1.matplotlib介绍 一个2D绘图库&#xff1b; 2.Pandas介绍&#xff1a; Pandas一个分析结构化数据的工具&#xff1b; 3.NumPy 一个处理n纬数组的包&#xff1b; 4.实践&#xff1a;绘图matplotlip figure()生成一个图像实例 %matplotlib inline&#xff1a;图形直接在…

前端传递list(数组)类型参数,后端接收失败

一顿报错,我之前遇到的list都是Long类型 貌似用GET也是可以的,但是很奇怪一直报错 就是不可以 后来去百度 查询到可以用两种方法解决这个问题 1、拆开 传 以GET方式&#xff0c;后端GetMappingRequestParam接收。 2、以Post方式传&#xff0c;后端创建dto PostMappingReques…

elementUI table表格相同元素合并行----支持多列

效果图如下: vue2代码如下&#xff1a; 只粘贴了js方法哦&#xff0c; methods: {// 设置合并行 setrowspans() { const columns [‘name’, ‘value’]; // 需要合并的列名 // 为每个需要合并的列设置默认 rowspan this.tableData.forEach(row > { columns.forEach(col …
最新文章