今天主要解决了一下测试账号登录状态的校验,我现在的方案是用户在写测试用例的时候使用特殊语法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();
}
}
附上事务隔离级别和传播行为的文档
隔离级别是指若干个并发的事务之间的隔离程度,与我们开发时候主要相关的场景包括:脏读取、重复读、幻读。
所谓事务的传播行为是指,如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为。
Fhaohaizi@163.com
。