前言

单例模式是一种比较常用的设计模式,目的是:保证一个类仅有一个实例,并提供一个访问它的全局访问点。

测试种可能用到的场景 :

在很多时候,有些对象我们希望在整个程序只有一个实例,如线程池、数据库连接池、缓存、日志对象、注册表等。而最近,在我的实际工作中,在编写接口自动化代码时就遇到了下列两种场景:

  1. 自动化所有用到的接口,在发送https请求时,都需要包含一个参数sessionId,该参数可以通过登录webserver的接口获取,我希望这个sessiondId是唯一的,且只需要获取一次。
  2. 由于系统的webserver是支持高可用的,即如果一个active webserver挂了,另一个standby webserver就会立即投入工作,此时web host就需要切换。为了支持高可用,我在发送请求时加入了兼容代码:如果捕获了连接异常(ConnectException)就会去尝试switchWebHost。在多线程并发执行测试用例的时候,我希望这个switchWebHost操作只需要执行一次。而如果将整个代码块加入synchronized同步,会导致不能同时发送https请求,导致并发量降低。
  3. 移动端或者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在测试中的运用的更多相关文章

  1. 设计模式之单例模式——Singleton

                        设计模式之单例模式--Singleton 设计意图: 保证类仅有一个实例,并且可以供应用程序全局使用.为了保证这一点,就需要这个类自己创建自己的对象,并且对外有 ...

  2. 【白话设计模式四】单例模式(Singleton)

    转自:https://my.oschina.net/xianggao/blog/616385 0 系列目录 白话设计模式 工厂模式 单例模式 [白话设计模式一]简单工厂模式(Simple Factor ...

  3. 浅谈设计模式--单例模式(Singleton Pattern)

    题外话:好久没写blog,做知识归纳整理了.本来设计模式就是个坑,各种文章也写烂了.不过,不是自己写的东西,缺少点知识的存在感.目前还没做到光看即能记住,得写.所以准备跳入设计模式这个大坑. 开篇先贡 ...

  4. java设计模式之 单例模式 Singleton

    static 的应用 单例模式 Singleton 单例:保证一个类在系统中最多只创建一个实例. 好处:由于过多创建对象实例,会产生过多的系统垃圾,需要GC频繁回收,由于GC会占用较大的系统资源,所有 ...

  5. Python设计模式 - 创建型 - 单例模式(Singleton) - 十种

    对于很多开发人员来说,单例模式算是比较简单常用.也是最早接触的设计模式了,仔细研究起来单例模式似乎又不像看起来那么简单.我们知道单例模式适用于提供全局唯一访问点,频繁需要创建及销毁对象等场合,的确方便 ...

  6. 抽象工厂(Abstract Factory),工厂方法(Factory Method),单例模式(Singleton Pattern)

    在谈工厂之前,先阐述一个观点:那就是在实际程序设计中,为了设计灵活的多态代码,代码中尽量不使用new去实例化一个对象,那么不使用new去实例化对象,剩下可用的方法就可以选择使用工厂方法,原型复制等去实 ...

  7. JAVA设计模式-单例模式(Singleton)线程安全与效率

    一,前言 单例模式详细大家都已经非常熟悉了,在文章单例模式的八种写法比较中,对单例模式的概念以及使用场景都做了很不错的说明.请在阅读本文之前,阅读一下这篇文章,因为本文就是按照这篇文章中的八种单例模式 ...

  8. ooad单例模式-Singleton

                                                单例模式Singleton 主要作用是保证在Java应用程序中,一个类Class只有一个实例存在. 比如建立目录 ...

  9. iOS单例模式(Singleton)写法简析

    单例模式的意思就是只有一个实例.单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例.这个类称为单例类. 1.单例模式的要点: 显然单例模式的要点有三个:一是某个类只能有一个实例: ...

随机推荐

  1. 继承Process类,计算累加和以及阶乘

    #定义一个类 继承Process类 from multiprocessing import Process import os class Download(Process): def __init_ ...

  2. Node.js实战5:操作系统与命令行。

    Nodejs有一些内置的方法可以查询操作系统信息: 如: process.arch获取到系统是32位还是64位, process.platform可获取系统的类型. 例程: console.log(p ...

  3. java Semaphore信号灯

    Semaphore实现的功能就类似2个公用电话,假如有10个人要打电话:那么只能2个人占有电话,8个需要等待.当2个人中 的任何一个人让开后,其中等待的另外8个人中又有一个人可以使用了等待的8个人中可 ...

  4. 爬取王垠的博客并生成pdf

    尚未完善,有待改进 #!/usr/bin/env python3 # -*- coding: utf-8 -*- __author__ = 'jiangwenwen' import pdfkit im ...

  5. Codeforces 1156E Special Segments of Permutation(单调栈)

    可以用单调栈直接维护出ai所能覆盖到的最大的左右范围是什么,然后我们可以用这个范围暴力的去查询这个区间的是否有满足的点对,一个小坑点,要对左右区间的大小进行判断,只需要去枚举距离i最近的一段区间去枚举 ...

  6. php cookie session 深究一下

    当一个用户用浏览器访问web(www.96net.com.cn)时候,若服务器开启session_start() 服务器tmp临时目录 自动生成session_id 并放回给创建一个cookie 保存 ...

  7. mySql配置在nodejs中使用

    mySql安装完成后,配置链接nodejs项目中的数据库. 1.测试是否安装成功. 2.use nodejs使用nodejs 3.设置数据源 5.exit

  8. elasticsearch 基础 —— Delete By Query API

    Delete By Query API _delete_by_query 的简单用法,就是在查询匹配到的每个文档上执行删除.例如: POST twitter/_delete_by_query { &q ...

  9. OC学习--继承

     1.什么是继承? 继承是指一个对象直接使用另一对象的属性和方法. 继承可以使得子类具有父类的各种属性和方法,而不是再次编写相同的代码.在子类继承父类的同时,可以重新定义某些属性,并重写某些方法, 即 ...

  10. macos系统安装mysql

    MacOS系统安装mysql 一.下载 官网下载链接地址:https://dev.mysql.com/downloads/mysql/ 二.安装 打开文件是pkg包,双击进行安装: 按照提示: 点击最 ...