• 单例模式@Singleton在测试中的运用


    前言

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

    测试种可能用到的场景 :

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

    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 

  • 相关阅读:
    反向代理实例
    nginx常用命令和配置
    nginx的安装
    Can Live View boot up images acquired from 64bit OS evidence?
    What is the behavior of lnk files?
    EnCase v7 search hits in compound files?
    How to search compound files
    iOS 8.3 JB ready
    Sunglasses
    现代福尔摩斯
  • 原文地址:https://www.cnblogs.com/Ronaldo-HD/p/11004581.html
Copyright © 2020-2023  润新知