为什么不推荐在自动化测试中使用单例模式

简述

  尽管在国内大量的代码中使用单例这种简单的方式,但在自动化测试过程中会导致很多问题。因此,在自动化测试中,不推荐使用单例模式。

  什么是单例?

  《设计模式:可复用面向对象软件的基础》一书(通常被称为 GOF 书籍)中描述的单例模式是一种确保一个类只有一个实例并提供全局访问点的方法。该模式规定,类本身应负责追踪其唯一的实例,并通过拦截创建新对象的请求来确保不能创建其他实例。

  简而言之,单例模式就是一个可以全局访问的唯一且不变的实例。这意味着程序的任何地方都可以方便地访问这个实例,并且无论何时访问它,都会得到同一个实例。

  示例代码

final class MySingleton {
      static let sharedInstance = MySingleton() // 静态常量保存唯一实例
      
      private init() {} // 私有化构造函数,确保外部无法直接实例化
      
      func someMethod() {
          print("This is a method of the singleton instance.")
      }
  }
  // 使用示例
  let singletonInstance = MySingleton.sharedInstance
  singletonInstance.someMethod() // 调用单例方法

 举个例子

struct LoggedInUser {}
  class ApiClient {
      static let shared = ApiClient() // 单例模式,存储唯一实例
      
      func login(completion: (LoggedInUser) -> Void) {
          // 模拟登录逻辑,实际情况应该是发送登录请求到服务器
          let loggedInUser = LoggedInUser()
          completion(loggedInUser)
      }
  }
  class MockApiClient: ApiClient {}
  class LoginViewController: UIViewController {
      var api = ApiClient.shared // 创建一个 ApiClient 实例
      
      func didTapLoginButton() {
          api.login() { user in
              print("User logged in:", user)
          }
      }
  }

单例的缺点

  在测试中使用单例模式可能会导致一些问题,这些问题包括:

  全局状态难以管理

  单例模式提供全局共享的实例,这使得在测试中难以管理和重置状态。不同的测试可能会相互干扰,因为它们共享同一个单例实例的状态。

// 测试示例
  class LoginViewControllerTests: XCTestCase {
      func testLogin() {
          let api = ApiClient.shared
          // 测试前重置单例状态
          api.reset() 
          
          // 执行测试
          let expectation = self.expectation(description: "Login")
          api.login { user in
              XCTAssertNotNil(user)
              expectation.fulfill()
          }
          waitForExpectations(timeout: 1, handler: nil)
          
          // 另一个测试
          api.reset() // 重置单例状态
          api.login { user in
              XCTAssertNotNil(user)
          }
      }
  }

难以进行并行测试

  由于单例实例在全局范围内是唯一的,这可能会导致测试之间的竞态条件,使得并行测试难以实现。如果一个测试修改了单例的状态,另一个同时运行的测试可能会失败,导致测试结果不可靠。

 // 测试示例
  class ParallelTests: XCTestCase {
      func testParallelLogin() {
          DispatchQueue.concurrentPerform(iterations: 10) { _ in
              let api = ApiClient.shared
              api.login { user in
                  XCTAssertNotNil(user)
              }
          }
      }
  }

测试隔离困难

  单例模式使得测试难以实现隔离。测试应该是独立的,彼此之间不应有任何副作用。但是由于单例实例是共享的,一个测试的改变可能会影响到其他测试,从而违反了测试的独立性原则。

// 测试示例
  class IsolatedTests: XCTestCase {
      func testIsolatedLogin() {
          let api1 = ApiClient.shared
          let api2 = ApiClient.shared
          api1.login { user in
              XCTAssertNotNil(user)
          }
          api2.login { user in
              XCTAssertNotNil(user)
          }
      }
  }

重置和初始化复杂

  在测试环境中需要对单例进行重置,以确保每个测试都有一个干净的初始状态。这可能需要额外的代码来处理单例实例的初始化和销毁,从而增加了测试的复杂性。

// 重置示例
  class ApiClient {
      static let shared = ApiClient()
      private var loggedInUser: LoggedInUser?
      
      func reset() {
          loggedInUser = nil
      }
      
      func login(completion: (LoggedInUser) -> Void) {
          if loggedInUser == nil {
              loggedInUser = LoggedInUser()
          }
          completion(loggedInUser!)
      }
  }

依赖隐藏

  单例模式使得依赖关系隐式化,难以通过构造函数注入等方式来明确声明依赖。这种隐式依赖关系会使得测试时很难替换单例实例,限制了对依赖的模拟(mocking)能力。

 // 依赖隐藏示例
  class LoginViewController: UIViewController {
      var api = ApiClient.shared // 隐式依赖
  }

增加代码耦合

  单例模式会增加代码之间的耦合度,因为多个类可能会依赖于同一个单例实例。这种耦合会使得测试某个类时,必须考虑到单例类的状态和行为,从而增加了测试的复杂性。

// 代码耦合示例
  class UserManager {
      func performLogin() {
          ApiClient.shared.login { user in
              print("User logged in:", user)
          }
      }
  }
  class UserManagerTests: XCTestCase {
      func testPerformLogin() {
          let userManager = UserManager()
          userManager.performLogin()
          // 需要考虑 ApiClient.shared 的状态
      }
  }

难以扩展和维护

  单例模式使得代码难以扩展和维护。如果业务逻辑发生变化,需要修改单例类,会影响到所有依赖该单例的测试,导致大量的测试需要更新。

// 难以扩展和维护示例
  class ApiClient {
      static let shared = ApiClient()
      func login(completion: (LoggedInUser) -> Void) {
          // 新的登录逻辑
          let loggedInUser = LoggedInUser()
          completion(loggedInUser)
      }
  }
  class ExtendedApiClient: ApiClient {
      override func login(completion: (LoggedInUser) -> Void) {
          // 新的扩展逻辑
          let loggedInUser = LoggedInUser()
          completion(loggedInUser)
      }
  }

总结

  在自动化测试中,尽管单例模式因其简洁和便捷而被广泛使用,但其在测试过程中却带来了诸多问题。单例模式虽然能确保类只有一个实例并能全局访问,但在测试场景中,会导致依赖性问题、状态共享、难以模拟和难以重置等挑战。这些问题会增加测试用例之间的耦合度,导致测试结果的不稳定和不确定性,并且使得测试编写和执行变得更加困难。

  通过避免使用单例模式,我们可以编写更健壮、更可靠的自动化测试,确保我们的代码在各种条件下都能正常运行。这不仅提高了代码质量,还增强了应用程序的稳定性和可维护性。

 

最后感谢每一个认真阅读我文章的人,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走:

 

这些资料,对于【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴上万个测试工程师们走过最艰难的路程,希望也能帮助到你!有需要的小伙伴可以点击下方小卡片领取   

 

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

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

相关文章

2024地理信息相关专业大学排名

在开始之前,不得不提一下今年福耀科技大学不能招生的遗憾,不知道明年是否能一切准备就绪开始招生呢? 如果这所大学能招生了,不知道它有没有地理信息相关专业呢? 言归正转,我们现在就基于公开资料&#xf…

vue:响应式原理解析,深入理解vue的响应式系统

一、文章秒读 vue的响应式系统核心有两个,简单描述就是: 1.在数据变化时重新render依赖相关函数(组件)。 2.在vue2和vue3中分别使用Object.defineProperty和Proxy进行对象属性的读写。 数据变化时: 二、什么是响应…

解决宝塔linux面板 - 404 Not Found(Nginx)方法

宝塔Linux面板后台登录提示404 Not Found Nginx如何解决?码笔记:这是因为BT面板丢失了安全登录入口,如下图: 宝塔404 Not Found nginx 解决方法: 1、先SSH远程服务器 2、然后执行命令 bt 14 重新获取宝塔面板URL地址安…

Linux安装frp实现内网穿透

Linux运维工具-ywtool 目录 一. 简介二.代理类型三.frp支持的Linux的架构四.安装1.准备工作2.配置frp服务器端(a)下载安装包(b)解压安装包(c)修改配置文件(d)启动服务端 3.配置frp客户端(a)下载安装包并修改配置文件(b)启动客户端 4.测试连接 五.其他1.多端口穿透(a)服务端(b)客…

wireshark工具获取设备IP地址

背景: 一个网口抓包工具,主要是升级XX设备时候不知道网口的ip地址。每次需要一个一个试,比较麻烦。 使用步骤: 1、连接好XX设备与笔记本,在网络连接里面找到以太网,没有出现红色X号,表示网线连…

【道合顺展会预告】2024国际传感器仪器仪表物联网长沙展览会!

传感器技术作为万物互联的基石,正以前所未有的速度驱动着全球各行各业的转型升级。在此背景下,2024国际传感器&仪器仪表&物联网展览会将于6月28日至30日在长沙盛大启幕,道合顺传感将携公司最新技术及科研成果参加展览会,并…

数据库自动备份到gitee上,实现数据自动化备份

本人有个不太好的习惯,每次项目的数据库都是在线上创建,Navicat 连接线上数据库进行处理,最近有一个项目需要二次升级,发现老项目部署的服务器到期了,完蛋,数据库咩了!!!…

开源模型应用落地-FastAPI-助力模型交互-WebSocket篇(一)

一、前言 使用 FastAPI 可以帮助我们更简单高效地部署 AI 交互业务。FastAPI 提供了快速构建 API 的能力,开发者可以轻松地定义模型需要的输入和输出格式,并编写好相应的业务逻辑。 FastAPI 的异步高性能架构,可以有效支持大量并发的预测请求,为用户提供流畅的交互体验。此外,F…

在Qt中,直接include <moc_xxxxx.cpp> 为什么不会出现符号冲突的错误?

在逛Qt官方社区的时候看到这样一个帖子: https://forum.qt.io/topic/117973/how-does-include-moc_-cpp-work 大概的意思是moc_xxx.cpp如果已经被编译器编译,那么在另一个cpp文件中include同一个moc_xxx.cpp应该出现符号冲突才对,但是Qt却能正…

关于如何更好管理好数据库的一点思考

本文尝试从数据库设计理论、ER图简介、性能优化、避免过度设计及权限管理方面进行思考阐述。 一、数据库范式 以下通过详细的示例说明数据库范式的概念,将逐步规范化一个例子,逐级说明每个范式的要求和变换过程。 示例:学生课程登记系统 初始…

提效优化:企业IT人员视角下的SD-WAN经验分享

我是公司IT支持人员,主要职责是确保公司的网络系统运行顺畅,让同事们能够顺利地完成他们的工作。随着公司业务的扩展和远程办公的普及,我工作中面临的挑战也日益严峻。 永无止境的问题流是我们IT人员日常工作中最为常见的现象。从“网络怎么这…

Spring Boot启动报错Lombok supports: sun/apple javac 1.6, ECJ

版本 idea 2023.3.4 <dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.32</version></dependency> 解决方式 File->Settings->Build, Execution, Deployment->Com…

录音转文字app哪个最好?5款实用的录音转文字软件下载

随着录音记录的普及&#xff0c;录音转文字功能已成为日常工作和学习中不可或缺的助手。 无论是会议记录、课堂笔记还是采访录音&#xff0c;这项技术都能快速、准确地将语音内容转换为文字。面对市场上众多的转文字软件&#xff0c;你知道录音转文字app哪个最好吗&#xff1f…

H5实现第三方分享功能,(WhatsApp,Facebook,Messenger,Instagram,Telegram,Zalo,Twitter/X)

1. H5实现第三方分享功能 1. WhatsApp 分享 https://api.whatsapp.com/send/?phone&app_absent0&text${codeUrl}2. Facebook 分享 https://www.facebook.com/sharer/sharer.php?u${codeUrl}3. Messenger 分享 https://www.messenger.com/?${codeUrl}4. Instagra…

Redis集群-计算key的插槽值等命令

文章目录 1、集群方式登录主机63792、计算key应该保存在那个插槽3、计算某个插槽中保存的key的数量4、返回指定槽中的键 1、集群方式登录主机6379 [rootlocalhost redis]# /usr/local/redis/bin/redis-cli -c -h 192.168.74.148 -p 6379 192.168.74.148:6379> keys * 1) &q…

2024年麒麟操作系统运维工程师(KYCP)培训介绍(培训费用+培训方式)

麒麟操作系统运维工程师&#xff08;KYCP&#xff09;培训是一个专注于提升学员在麒麟操作系统运维技能方面的综合性课程。以下是关于该培训课程的清晰概述&#xff1a; 1. 培训课程概述 内容涵盖&#xff1a;安全管理、日志管理、系统监控、服务部署等多个方面的知识内容。目…

Qt-Advanced-Docking-System示例程序

写了一些简单的示例程序&#xff0c;帮助我更好地使用和了解Qt-Advanced-Docking-System 1.写一个如图页面布局的程序 m_pDockMangernew ads::CDockManager(this);this->setCentralWidget(m_pDockManger);ads::CDockWidget* centerDockWidgetnew ads::CDockWidget("中…

麦克风哪个品牌音质最好,领夹麦克风十大品牌排行榜

​随着科技的不断进步&#xff0c;无线领夹麦克风在各类场合都展现出强大实力。无论是直播、采访还是教学&#xff0c;无线领夹麦克风都成为媒体人创作的重要工具。对于追求作品质量的媒体人来说&#xff0c;选择一款性能优异、价格合理的无线领夹麦克风至关重要。因此&#xf…

中小学校共用电脑通过安当SLA产品配置实现开机控制

中小学校公用电脑实现电脑开机控制的必要性主要体现在以下几个方面&#xff1a; 1. 增强安全性&#xff1a; 公用电脑由于使用频繁&#xff0c;容易被未经授权的用户访问&#xff0c;可能存在数据泄露或恶意软件植入的风险。通过实现电脑开机控制&#xff0c;学校可以确保只有…

如何利用自助式商业智能(BI)打破组织中的数据孤岛?

前言 许多组织都存在数据问题。当许多员工远程工作&#xff08;或在混合环境中&#xff09;并在多个位置使用多个设备访问公司数据时&#xff0c;他们正在处理信息过载问题。这只会加剧数据孤岛的问题。 数据孤岛正是它听起来的样子&#xff1a;孤立在一个孤立的用户/环境中的…