为了账号安全,请及时绑定邮箱和手机立即绑定

使用资源锁进行并行测试?

使用资源锁进行并行测试?

慕姐4208626 2023-01-05 10:03:55

背景:现在使用java JUnit4,愿意迁移到JUnit5或TestNG。

当前状态:拥有 100 多个 Selenium 测试。其中大部分通过 Junit4 中的 @RunWith(Parameterized.class) 重复。(即根据提供的参数集创建测试类的多个实例,通常是浏览器类型 + 用户身份的组合。)共享大约 12 个用户的有限集合。

限制:被测试的应用程序阻止同一用户同时在多个地方登录。因此,如果用户在一个线程中运行的某个测试中登录应用程序,则会导致同一用户在同一时刻在另一个线程中运行的另一个测试中立即注销。

问题:当并行执行的测试无法共享某些资源时,是否有任何推荐的方法来管理线程安全?或者如何强制使用相同资源的那些测试在同一个线程中执行?

感谢您的想法。


这是到目前为止我用 TestNG 找到的一些解决方案的简化示例......:

public abstract class BaseTestCase {

    protected static ThreadLocal<WebDriver> threadLocalDriver = new ThreadLocal<>();

    protected String testUserName;


    private static final Set<String> inUse = new HashSet<>();


    public BaseTestCase(WebDriver driver, String testUserName) {

        threadLocalDriver.set(driver);

        this.testUserName = testUserName;

    }


    private boolean syncedAddUse(@NotNull String key){

        synchronized (inUse){

            return inUse.add(key);

        }

    }


    private boolean syncedRemoveUse(@NotNull String key){

        synchronized (inUse) {

            return inUse.remove(key);

        }

    }


    @DataProvider(parallel = true)

    public static Object[][] provideTestData() {

        //load pairs WebDriver+user from config file. E.g.:

        //Chrome + chromeUser

        //Chrome + chromeAdmin

        //Firefox + firefoxUser

        //etc...

    }


    @BeforeMethod

    public void syncPoint() throws InterruptedException {

        while( !syncedAddUse(testUserName) ){

            //Waiting due the testUserName is already in use at the moment.

            Thread.sleep(1000);

        }

    }


    @AfterMethod

    public void leaveSyncPoint(){

        syncedRemoveUse(testUserName);

    }

}


查看完整描述

1 回答

?
守着一只汪

TA贡献1621条经验 获得超3个赞

我不知道有什么方法可以在每个组都在一个线程中运行的组中组织测试。但是您可以用“尝试锁定用户”替换“用户忙时睡眠”。一旦对用户完成另一个测试(即解锁锁),后者就会继续执行。


下面的可运行示例应该让您开始了解“尝试锁定用户”的想法。请记住,如果您获得了锁(在您的情况下为“beforeTest”),您必须确保在“finally”块(在您的情况下为“afterTest”)中释放锁。否则执行可能会挂起并且永远不会完成。


import java.util.Map;

import java.util.Random;

import java.util.concurrent.ConcurrentHashMap;

import java.util.concurrent.CountDownLatch;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

import java.util.concurrent.locks.ReentrantLock;

import java.util.concurrent.TimeUnit;

import java.util.stream.IntStream;


// https://stackoverflow.com/questions/56474713/parallel-tests-with-resource-lock

public class NamedResourceLocks {


    public static void main(String[] args) {


        System.out.println("Starting");

        ExecutorService executor = Executors.newCachedThreadPool();

        try {

            new NamedResourceLocks().run(executor);

        } catch (Exception e) {

            e.printStackTrace();

        } finally {

            executor.shutdownNow();

        }

        System.out.println("Done");

    }


    final static String userPrefix = "user";

    final static int maxUsers = 3;

    final static long maxWait = 10_000; // 10 seconds

    final static long startTime = System.currentTimeMillis();


    final Map<String, ReentrantLock> userLocks = new ConcurrentHashMap<>();

    final int maxTests = maxUsers * 10;

    final CountDownLatch allTestsDone = new CountDownLatch(maxTests);


    void run(ExecutorService executor) throws Exception {


        IntStream.range(0,  maxUsers).forEach(u -> 

            userLocks.put(userPrefix + u, new ReentrantLock(true)));

        IntStream.range(0,  maxTests).forEach(t -> 

            executor.execute(new Test(this, random.nextInt(maxUsers), t)));

        if (allTestsDone.await(maxWait, TimeUnit.MILLISECONDS)) {

            System.out.println("All tests finished");

        }

    }



    void lock(String user) throws Exception {


        ReentrantLock lock = userLocks.get(user);

        if (!lock.tryLock(maxWait, TimeUnit.MILLISECONDS)) {

            throw new RuntimeException("Waited too long.");

        }

    }


    void unlock(String user) {


        userLocks.get(user).unlock();

    }


    void oneTestDone() {


        allTestsDone.countDown();

    }


    final static Random random = new Random();


    static class Test implements Runnable {


        final NamedResourceLocks locks;

        final String user;

        final int testNumber;


        public Test(NamedResourceLocks locks, int userNumber, int testNumber) {

            this.locks = locks;

            this.user = userPrefix + userNumber;

            this.testNumber = testNumber;

        }


        @Override

        public void run() {


            boolean haveLock = false;

            try {

                log(this, "acquiring lock");

                locks.lock(user);

                haveLock = true;

                int sleepTime = random.nextInt(maxUsers) + 1; 

                log(this, "sleeping for " + sleepTime + " ms.");

                Thread.sleep(sleepTime);

            } catch (Exception e) {

                e.printStackTrace();

            } finally {

                if (haveLock) {

                    log(this, "releasing lock");

                    locks.unlock(user);

                }

                locks.oneTestDone();

            }

        }


    }


    static void log(Test test, String msg) {

        System.out.println((System.currentTimeMillis() - startTime) + " - " +

                test.testNumber + " / " + test.user + " - " + msg);

    }

}



查看完整回答
反对 回复 2023-01-05

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信