前言

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

测试种可能用到的场景 :

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

  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. .NET简单三层的理解

    1.UI 表示层 :就是我们看到的网站前台2.BLL 业务逻辑层:很简单 也很重要 处理逻辑问题 简单程序看不出啥效果3.DAL 数据访问层: 写数据连接和执行的SQL语句4.MODEL 模型层:封装 ...

  2. 图解Http阅读笔记(二)

    简单的HTTP协议 HTTP是一种不保存状态,即无状态(stateless)协议.HTTP 协议自身不对请求和响应之间的通信状态进行保存.也就是说在 HTTP 这个级别,协议对于发送过的请求或响应都不 ...

  3. Pull Request的正确打开方式(如何在GitHub上贡献开源项目)

    Pull Request的正确打开方式(如何在GitHub上贡献开源项目) GitHub的官方帮助如下: Fork A Repo: https://help.github.com/articles/f ...

  4. 【问题解决方案】git中的文件的重命名

    环境: win7 git bash+GitHub 问题: 如果直接在文件夹中手动重命名,Git会判定为删除了旧文件,添加了新文件 版本回退后使用mv命令进行重命名 $ git reset --hard ...

  5. JSONP面试

    jQuery 的 JSONP的原理是动态创建一个 script 标签,利用src 发送请求,获取数据 回调函数的键名叫做  callback 跟ajax没有关系 JSONP:主要是利用 script标 ...

  6. 2018-8-10-win10-uwp-MetroLog-入门

    title author date CreateTime categories win10 uwp MetroLog 入门 lindexi 2018-08-10 19:16:53 +0800 2018 ...

  7. 一、Api

    一. private static readonly IList<string> BaseParamKey = new List<string>() { "apiId ...

  8. ERROR- 开发常见error

    一,数据插入MySql中出现中文乱码 解决办法有: 1.新建数据库选择 create database 'GG' CHARACTER SET 'utf8 ' COLLATE 'utf8_general ...

  9. .bat 文件调用python脚本

    1.将clearlog.py 脚本放在指定目录 比如 我放在 C:\Users\Administrator\Desktop 上 也就是桌面上 2.创建一个.bat 位后缀名的脚本 3.写入如下脚本 @ ...

  10. 【leetcode】636. Exclusive Time of Functions

    题目如下: 解题思路:本题和括号匹配问题有点像,用栈比较适合.一个元素入栈前,如果自己的状态是“start”,则直接入栈:如果是end则判断和栈顶的元素是否id相同并且状态是“start”,如果满足这 ...