单例模式@Singleton在测试中的运用
前言
单例模式是一种比较常用的设计模式,目的是:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
测试种可能用到的场景 :
在很多时候,有些对象我们希望在整个程序只有一个实例,如线程池、数据库连接池、缓存、日志对象、注册表等。而最近,在我的实际工作中,在编写接口自动化代码时就遇到了下列两种场景:
- 自动化所有用到的接口,在发送https请求时,都需要包含一个参数sessionId,该参数可以通过登录webserver的接口获取,我希望这个sessiondId是唯一的,且只需要获取一次。
- 由于系统的webserver是支持高可用的,即如果一个active webserver挂了,另一个standby webserver就会立即投入工作,此时web host就需要切换。为了支持高可用,我在发送请求时加入了兼容代码:如果捕获了连接异常(ConnectException)就会去尝试switchWebHost。在多线程并发执行测试用例的时候,我希望这个switchWebHost操作只需要执行一次。而如果将整个代码块加入synchronized同步,会导致不能同时发送https请求,导致并发量降低。
- 移动端或者WEB自动化测试的时候,初始化driver(常见的webdriver,appium下的Androiddriver 等)的时候,我们也希望可以实例化,全局方法可以访问,但是只执行一次
借用单例模式或借鉴其思想就可以解决上述问题。
简单单例模式:
public class Singleton{
private static Singleton uniqueInstance;
private Singleton(){}
public static Singleton getInstance(){
if (null==uniqueInstance){
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
上面给出的是Singleton 懒汉式(lazy-loading)实现,Singleton类拥有一个静态变量uniqueInstance来记录Singleton的唯一实例,注意它的构造函数是private的,这就注定了只有Sinleton类内才可以使用该构造器。在其他类中我们无法通过new Singleton()的方式类获取一个Singleton的实例,只能通过Singleton.getInstance()的方式获取。并且由于uniqueInstance是一个静态变量,属于Singleton这个类,所以保证了其唯一性。
经典模式有个好处,就是它的对象的实例化只有等到getInstance方法被调用时才会被jvm加载,如果getInstance始终没有被调用,jvm就不会生成该实例。如果该对象的实例化需要消耗较多的资源,这种“延迟实例化”的方式可以减小jvm的开销。
但是,上述的实现方式很容易会想到存在一个严重的缺陷,就是“非线程安全”。不能运用于多线程环境,当多个线程同时调用Singleton.getInstance()来获取实例时,uniqueInstance对象就可能被多次实例化。最简单的方式就是通过synchronized关键字来实现线程同步:
public static synchronized Singleton getInstance(){
if (null==uniqueInstance){
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
Double-Checked Locking实现(DCL) :
public class Singleton{
private volatile static Singleton uniqueInstance;
private Singleton(){}
public Singleton getInstance(){
if (null == uniqueInstance){
synchronized (Singleton.class){
if( null == uniqueInstance){
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
}
这种方式也是将实例化延迟到了getInstance方法被调用时,区别于经典单例模式,该方式引入了“双重检查(Double-Checked Locking)”,
在多线程并行执行到同步代码块时,会再次判断uniqueInsance是否为null,有效防止了多次实例化的发生。
并且这种方式并没有对整个getInstance方法加锁,只是在第一次生成Singleton的唯一实例时进行了一次同步,并没有降低程序的并发性。
直接同步整个getInstance()方法产生性能低下的原因是,在判断(instance==null)时,所有线程都必须等待。而(instance==null)并非是常有情况,每次判断都必须等待,会造成阻塞。
因此,有了这种双重检测的实现方法,待检查到实例没创建后(instance=null),再进行同步,然后再检查一次确保实例没创建。
静态内部类方式
静态内部类的方式也可以实现同时满足性能和并发要求的单例模式。
public class Singleton{
private static class Holder{
private static final Singleton INSTANCE = new Singleton();
}
private Singleton(){}
public static final Singleton getInstance(){
return Holder.INSTANCE;
}
}
解决接口自动化里面的sessionid的获取:
使用“静态内部类”方法创建SessionFactory类:
public class SessionFactory {
private static String sessionId;
private static BaseConfig baseConfig = BaseConfigFactory.getInstance(); private static class SessionidHolder{
private final static SessionFactory INSTANCE = new SessionFactory();
} public static final SessionFactory getInstance(){
return SessionidHolder.INSTANCE;
}
private SessionFactory(){
LoginApi api = new LoginApi();
String username = baseConfig.getLoginUsername();
String password = baseConfig.getLoginPassword();
sessionId= api.login(username, password).getValue("session.id"); /** 获取session.id,在api.login类中 **/
} public String getSessionId() {
return sessionId;
}
}
使用Testng编写测试用例:
public class SessionTest {
@Test(threadPoolSize=10, invocationCount=10)
/** 预期: 10个线程并发执行时,session.id是唯一的,说明sessionFactory是唯一的,只被实例化了一次。**/
public void sessionTest(){
SessionFactory sessionFactory = SessionFactory.getInstance();
System.out.println("Thread id="+ Thread.currentThread().getId()+
", session.id=" + sessionFactory.getSessionId());
}
}
遇到webserver切换时,希望switchWebHost操作只需要执行一次
/**
切换webhost部分的代码进行同步,并且在切换时先通过调用isWebHostChanged()方法判断是否已经被其他线程切换。
防止host多次发生切换。同时,这种方式不会影响到sendHttpsRequest方法的并发。
**/
try {
sendHttpsRequest();
}catch(ConnectException e){
numRquestFail++;
synchronized (BaseApi.class) {
if (isWebHostChanged()){
return;
}
switchWebHost(actualUrl, numRequestFail, e);
}
}
单例模式封装webdriver
同理,该模式可以运用到androiddriver
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.ie.InternetExplorerDriver; public class SingletonWebDriver {
private static WebDriver instance = null;
public static WebDriver getWebDriver(String browserDriverType,String browserDriverPath) {
if(instance == null) {
if("webdriver.chrome.driver".equalsIgnoreCase(browserDriverType)) {
System.setProperty(browserDriverType, browserDriverPath);
instance = new ChromeDriver();
}else if ("webdriver.ie.driver".equalsIgnoreCase(browserDriverType)) {
System.setProperty(browserDriverType, browserDriverPath);
instance = new InternetExplorerDriver();
}else if ("webdriver.firefox.bin".equalsIgnoreCase(browserDriverType)) {
System.setProperty(browserDriverType, browserDriverPath);
instance = new FirefoxDriver();
}
}
return instance;
}
}
单例可参考: https://www.cnblogs.com/techyc/p/3529983.html
单例模式@Singleton在测试中的运用的更多相关文章
- 设计模式之单例模式——Singleton
设计模式之单例模式--Singleton 设计意图: 保证类仅有一个实例,并且可以供应用程序全局使用.为了保证这一点,就需要这个类自己创建自己的对象,并且对外有 ...
- 【白话设计模式四】单例模式(Singleton)
转自:https://my.oschina.net/xianggao/blog/616385 0 系列目录 白话设计模式 工厂模式 单例模式 [白话设计模式一]简单工厂模式(Simple Factor ...
- 浅谈设计模式--单例模式(Singleton Pattern)
题外话:好久没写blog,做知识归纳整理了.本来设计模式就是个坑,各种文章也写烂了.不过,不是自己写的东西,缺少点知识的存在感.目前还没做到光看即能记住,得写.所以准备跳入设计模式这个大坑. 开篇先贡 ...
- java设计模式之 单例模式 Singleton
static 的应用 单例模式 Singleton 单例:保证一个类在系统中最多只创建一个实例. 好处:由于过多创建对象实例,会产生过多的系统垃圾,需要GC频繁回收,由于GC会占用较大的系统资源,所有 ...
- Python设计模式 - 创建型 - 单例模式(Singleton) - 十种
对于很多开发人员来说,单例模式算是比较简单常用.也是最早接触的设计模式了,仔细研究起来单例模式似乎又不像看起来那么简单.我们知道单例模式适用于提供全局唯一访问点,频繁需要创建及销毁对象等场合,的确方便 ...
- 抽象工厂(Abstract Factory),工厂方法(Factory Method),单例模式(Singleton Pattern)
在谈工厂之前,先阐述一个观点:那就是在实际程序设计中,为了设计灵活的多态代码,代码中尽量不使用new去实例化一个对象,那么不使用new去实例化对象,剩下可用的方法就可以选择使用工厂方法,原型复制等去实 ...
- JAVA设计模式-单例模式(Singleton)线程安全与效率
一,前言 单例模式详细大家都已经非常熟悉了,在文章单例模式的八种写法比较中,对单例模式的概念以及使用场景都做了很不错的说明.请在阅读本文之前,阅读一下这篇文章,因为本文就是按照这篇文章中的八种单例模式 ...
- ooad单例模式-Singleton
单例模式Singleton 主要作用是保证在Java应用程序中,一个类Class只有一个实例存在. 比如建立目录 ...
- iOS单例模式(Singleton)写法简析
单例模式的意思就是只有一个实例.单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例.这个类称为单例类. 1.单例模式的要点: 显然单例模式的要点有三个:一是某个类只能有一个实例: ...
随机推荐
- HTML/CSS点滴记录
1. overflow 定义溢出元素内容区的内容会如何处理 visible 默认值.内容不会被修剪,会呈现在元素框之外. hidden 内容会被修剪,并且其余内容是不可见的. scroll 内容会被修 ...
- 爬虫(十一)—— XPath总结
目录 XPath总结 一.何为XPath 二.XPath语法 1.语法 2.实例 三.XPath轴 1.XPath轴语法 2.XPath轴实例 四.XPath运算符 XPath总结 一.何为XPath ...
- python3入门之基础语法
Python 是一个高层次的结合了解释性.编译性.互动性和面向对象的脚本语言.Python 的设计具有很强的可读性,相比其他语言经常使用英文关键字,其他语言的一些标点符号,它具有比其他语言更有特色语法 ...
- spring security简单教程以及实现完全前后端分离
spring security是spring家族的一个安全框架,入门简单.对比shiro,它自带登录页面,自动完成登录操作.权限过滤时支持http方法过滤. 在新手入门使用时,只需要简单的配置,即可实 ...
- [已解决]报错: Error response from daemon: conflict
报错内容: Error response from daemon: conflict: unable to delete f5b6ef70d79b (must be forced) - image i ...
- [洛谷P1552] [APIO2012]派遣(左偏树)
这道题是我做的左偏树的入门题,奈何还是看了zsy大佬的题解才能过,唉,我太弱了. 左偏树总结 Part 1 理解题目 很显然,通过管理关系的不断连边,最后连出来的肯定是一棵树,那么不难得出,当一个忍者 ...
- 如何将网络流转化为内存流 C#
//将获取的文件流转化为内存流 public static MemoryStream ConvertStreamToMemoryStream(Stream stream) { MemoryStream ...
- C语言文件读写操作
C语言实现文件读写,注意区分几个方法: 写入: fwrite() //个人认为这个最好,可是实现写入任何数据类型,任何长度 fputs() //写入一个字符串,字符串长度不能太长,具体的长度未知,但估 ...
- Kintex7 XC7K325T 板卡五兄弟
Kintex 7五兄弟 1. 基KC705E 增强版基于FMC接口的Xilinx Kintex-7 FPGA K7 XC7K325T PCIeX8 接口卡(136) 本板卡是Xilinx公司芯 ...
- shortcut to add throws declaration in Intellij Idea
When a piece of code needs error handling, IntelliJ underlines it with red. Set your pointer on that ...