• 自动化框架参数间传递的一种最佳实践


    持续坚持原创输出,点击蓝字关注我吧

    作者:软件质量保障
    知乎:https://www.zhihu.com/people/iloverain1024

    本文的主题是ThreadLocal,作者将其用于接口自动化框架设计,并解决用例并发执行带来的线程不安全问题。

    首先介绍ThreadLocal的原理与使用,其次介绍使用场景以及接口自动化的简单演示。

    一、解析ThreadLocal

    ThreadLocal中填充的变量属于当前线程,该变量对其他线程而言是隔离的,也就是说该变量是当前线程独有的变量。ThreadLocal为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量,可以称为线程本地变量。

    查看ThreadLocal实现源码,会发现有四个方法比较重要。

    public T get()public void set(T value)protected T initialValue()public void remove()

    下面通过实例给大家演示下ThreadLocal的原理与用法。

    set就是设置值,get就是获取值,如果没有值,返回null,看上去,ThreadLocal就是一个单一对象的容器,比如:

    public class ThreadLocalBasic {    static ThreadLocal<Integer> local = new ThreadLocal<>();    public static void main(String[] args) throws InterruptedException{        // main 线程,设置local值为100        local.set(100);        Thread child = new Thread(){            // Thread-0 线程            @Override            public void run(){                System.out.println(Thread.currentThread().getName() + ": "+ local.get());                local.set(200);                System.out.println(Thread.currentThread().getName() + ": "+local.get());            }        };        child.start();        child.join();        System.out.println(Thread.currentThread().getName()+ ": " + local.get());    }}

    5320bfb5f3a46fee4fb6a050f84bca9b.png

    通过结果发现,main线程对local变量的设置对Thread-0线程不起作用,Thread-0线程对local变量的改变也不会影响main线程,它们访问的虽然是同一个变量local,但每个线程都有自己的独立的值,这就是线程本地变量的含义。

    initialValue用于提供初始值,可以通过匿名内部类的方式提供,当调用get方法时,如果之前没有设置过,会调用该方法获取初始值,默认实现是返回null。remove删掉当前线程对应的值,如果删掉后,再次调用get,会再调用initialValue获取初始值。

    public class ThreadLocalInit {    static ThreadLocal<Integer> local = new ThreadLocal<Integer>(){        @Override        protected Integer initialValue(){            return 100;        }    };    public static void main(String[] args) {        System.out.println(local.get());        local.set(200);        local.remove();        System.out.println(local.get());    }}

    44bf889ce8f69dbb40e9b6e5ede66ccd.png

    二、ThreadLocal使用场景

    日期处理

    ThreadLocal是实现线程安全的一种方案,比如对于DateFormat/SimpleDateFormat,每个线程使用自己的DateFormat,就不存在安全问题了,在线程的整个使用过程中,只需要创建一次,又避免了频繁创建的开销。

    public class ThreadLocalDateFormat {    static ThreadLocal<DateFormat> sdf = new ThreadLocal<DateFormat>() {        @Override        protected DateFormat initialValue() {            return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");    }    };    public static String date2String(Date date){        return sdf.get().format(date);    }    public static Date string2Date(String str) throws ParseException{        return sdf.get().parse(str);    }}

    三、接口自动化上下文信息

    ThreadLocal的典型用途是提供上下文信息,比如在一个Web服务器中,一个线程执行用户的请求,在执行过程中,很多代码都会访问一些共同的信息,比如请求信息、用户身份信息、数据库连接、当前事务等,它们是线程执行过程中的全局信息,如果作为参数在不同代码间传递,使用ThreadLocal就很方便,这样又在并发的场景下保证了线程安全。

    下面以登录接口,测试查询接口为例,登录后直接将token存入ThreadLocal的token中,然后在queryTest使用token传递给queryBook接口用于鉴权。

    public class ThreadLocalDemoTest {    static ThreadLocal<String> token = new ThreadLocal<String>();    // 模拟登陆,将token set到上下文    @BeforeTest    public static void login(){        token.set(LoginService.login("admin"));    }    @Test    public void queryTest(){        String response = QueryBookService.queryBook(token.get());        Assert.assertEquals(response, "管理员查询图书");    }}

    当然了,上述例子比较简单,线程本地变量仅存储token;如果需要存储更多的信息,那该咋办?

    我们可以对ThreadLocal进行封装成ThreadLocalUtil,开发一个用于存储map的put和取map的getContextMap方法。

    private final static ThreadLocal<Map<String, Object>> THREAD_CONTEXT = new MapThreadLocal();public static void put(String key, Object value) {        getContextMap().put(key, value);}static Map<String, Object> getContextMap() {        return THREAD_CONTEXT.get();    }

    用例实践:

    public class ThreadLocalTest {    // 模拟登陆,将token set到上下文    @BeforeTest    public static void login(){        ThreadLocalUtil.put("token", LoginService.login("admin"));        ThreadLocalUtil.put("username", "软件质量保障");    }    @Test    public void queryTest(){        String response = QueryBookService.queryBook(ThreadLocalUtil.getContextMap().get("token").toString());        Assert.assertEquals(response, "管理员查询图书");    }}

    - END -


    下方扫码关注 软件质量保障,与质量君一起学习成长、共同进步,做一个职场最贵Tester!

    • 后台回复【测开】获取测试开发xmind脑图

    • 后台回复【加群】获取加入测试社群!

    往期推荐

    聊聊工作中的自我管理和向上管理

    经验分享|测试工程师转型测试开发历程

    聊聊UI自动化的PageObject设计模式

    细读《阿里测试之道》

    我在阿里做测开

  • 相关阅读:
    GaussDB(DWS)云原生数仓技术解析:湖仓一体,体验与大数据互联互通
    NLG(自然语言生成)评估指标介绍
    Cheat Engine.exe修改植物大战僵尸阳光与冷却
    8. Python 面向对象
    22服务-ReadDataByIdentifier
    学习WCET(一)
    图片的谱表征
    FileInputStream(文件输入流)
    数学术语之源——群同态的“核(kernel)”
    java线程简介
  • 原文地址:https://blog.csdn.net/csd11311/article/details/126552106