今天主要解决了一下测试账号登录状态的校验,我现在的方案是用户在写测试用例的时候使用特殊语法uid=123这样的形式,表示该用例字段应该去uid等于123的测试账号的登录凭证。难点在于登录凭证会过期,会被挤掉,如果维护所有测试用户的登录状态又很麻烦,对服务器性能也是一种浪费。

所以最终的方案如下:

在并行运行测试用例的时候,如果保证所以线程都能读到最新的用户凭证,且往缓存map中读取的数据正确性,想了一个方案就是在JVM里面对每一个用户进行加锁的操作,保证每一次只有一个线程去读写用户登录凭证。这样在单个用户运行的时候就可以登录一次,短时间内不用去登录即可持续进行用例的调试。在运行用例集的时候,每次运行创建一个临时的map存放本次用到的用户登录凭证,用例集内的测试用例执行完,该对象就释放,等着被GC回收。

用户对象锁存放类

package com.okay.family.common.basedata;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.ConcurrentHashMap;

public class UserCertificate {

    private static Logger logger = LoggerFactory.getLogger(UserCertificate.class);

    private static ConcurrentHashMap<Integer, Object> certificates = new ConcurrentHashMap<>();

    /**
     * 获取锁对象,用户测试用户锁
     *
     * @param id
     * @return
     */
    public static Object get(int id) {
        certificates.compute(id, (key, value) ->
        {
            if (value == null) {
                value = new Object();
            }
            return value;
        });
        return certificates.get(id);
    }


}

业务实现类

@Override
@Transactional(isolation = Isolation.REPEATABLE_READ, propagation = Propagation.REQUIRES_NEW)
public TestUserCheckBean getCertificate(int id) {
    Object o = UserCertificate.get(id);
    synchronized (o) {
        TestUserCheckBean user = testUserMapper.findUser(id);
        String create_time = user.getCreate_time();
        long create = Time.getTimestamp(create_time);
        long now = Time.getTimeStamp();
        if (now - create < OkayConstant.CERTIFICATE_TIMEOUT && user.getStatus() == UserState.OK.getCode())
            return user;
        boolean b = UserUtil.checkUserLoginStatus(user);
        if (!b) {
            UserUtil.updateUserStatus(user);
        }
        testUserMapper.updateUserStatus(user);
        return user;
    }
}

@Override
public String getCertificate(int id, ConcurrentHashMap<Integer, String> map) {
    Object o = UserCertificate.get(id);
    synchronized (o) {
        if (map.contains(id)) return map.get(id);
        TestUserCheckBean user = testUserMapper.findUser(id);
        String create_time = user.getCreate_time();
        long create = Time.getTimestamp(create_time);
        long now = Time.getTimeStamp();
        if (now - create < OkayConstant.CERTIFICATE_TIMEOUT && user.getStatus() == UserState.OK.getCode()) {
            map.put(id, user.getCertificate());
            return user.getCertificate();
        }
        boolean b = UserUtil.checkUserLoginStatus(user);
        if (!b) {
            UserUtil.updateUserStatus(user);
            if (user.getStatus()!=UserState.OK.getCode()) UserStatusException.fail();
        }
        map.put(id, user.getCertificate());
        testUserMapper.updateUserStatus(user);
        return user.getCertificate();
    }
}

附上事务隔离级别和传播行为的文档

事务隔离级别

隔离级别是指若干个并发的事务之间的隔离程度,与我们开发时候主要相关的场景包括:脏读取、重复读、幻读。

传播行为

所谓事务的传播行为是指,如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为。



↙↙↙阅读原文可查看相关链接,并与作者交流