我有UI自动化测试。测试涉及三个实体 -
数据对象类 - 要填写表单的数据。在此,页面上的每个表单都可以由不同的数据对象表示 帮助程序类 - 在页面上填写表单中的数据 测试类 - 使用数据对象和助手类来执行测试。
以下是测试的缩减版本 -
public class ParallelDataObject {
HelperClass helperClass = new HelperClass();
Data data;
@BeforeMethod
public void setTestData() {
data = new Data();
helperClass.setData(data);
}
@Test
public void passM1() {
helperClass.verifyFlag();
}
@Test
public void failM2() {
data.setFlag(false);
helperClass.setData(data);
helperClass.verifyFlag();
}
@Test
public void passM3() {
helperClass.verifyFlag();
}
@Test
public void failM4() {
data.setFlag(false);
helperClass.setData(data);
helperClass.verifyFlag();
}
}
class HelperClass {
Data data;
public void setData(Data data) {
synchronized (data) {
this.data = data;
}
}
public void verifyFlag() {
synchronized (data) {
assert data.getFlag();
}
}
}
class Data {
private boolean flag;
public Data() {
flag = true;
}
public Data setFlag(boolean flag) {
synchronized (this) {
this.flag = flag;
return this;
}
}
public boolean getFlag() {
synchronized (this) {
return flag;
}
}
当并行执行方法时,我遇到了奇怪的结果,因为数据不是线程安全的。然后我合并了同步块,但我遇到了奇怪的结果。 我确信我已经搞砸了如何在这里使用同步。任何见解?
我又做了一次运动。我设置了另一个与第一个测试类完全相同的Test类。我从helper和data类中删除了所有同步。当我并行运行类(而不是方法)。测试结果如预期。当我并行执行类时,为什么不运行并发,即使它们使用相同的辅助类和数据对象?
答案 0 :(得分:2)
HelperClass
和Data
是线程安全的。
问题是你的一些测试方法会执行几个操作。并且测试方法中的操作序列不是原子的,只要它不同步即可。
例如,在failM4
执行期间,helperClass
的状态可能会被其他线程修改。
我建议您不要在测试方法之间使用共享状态,因为同步会使并发测试执行的优势无效。
答案 1 :(得分:1)
考虑使用ThreadLocal
。这样每个线程都有自己的HelperClass
副本。请注意,同步单独的方法不会给你任何东西 - 在一个测试中(在一个线程中)所做的更改可被其他测试看到
class ParallelDataObject {
private final ThreadLocal<HelperClass> helperClassThreadLocal = new ThreadLocal<HelperClass>() {
@Override
protected HelperClass initialValue() {
return new HelperClass(new Data());
}
};
private HelperClass helperClass() {
return helperClassThreadLocal.get();
}
@Test
public void passM1() {
helperClass().verifyFlag();
}
@Test
public void failM2() {
helperClass().getData().setFlag(false);
helperClass().verifyFlag();
}
}
class HelperClass {
private final Data data;
public HelperClass(Data data) {
this.data = data;
}
public Data getData() {
return data;
}
public void verifyFlag() {
assert data.getFlag();
}
}
class Data {
private boolean flag = true;
public Data setFlag(boolean flag) {
this.flag = flag;
return this;
}
public boolean getFlag() {
return flag;
}
}
passM3
和failM4
是多余的HelperClass
需要Data
的实例才能工作,所以它应该使用构造函数依赖项声明它使用时:
synchronized(this)
包装整个方法体,考虑在方法声明中使用synchronized
关键字(更易读)。
ThreadLocal
s
@gpeche 提出了一个很好的建议,即测试应该是独立的。不幸的是(为什么,为什么!?)JUnit重复使用相同的测试用例类实例(在这种情况下为ParallelDataObject
)来执行所有测试方法。这意味着将任何有状态对象分配给测试用例类字段是危险的,必须避免。
在这种特殊情况下, OP必须在每个测试方法中创建一个新的HelperClass实例(事实上,这并不是一个坏主意):
class ParallelDataObject {
@Test
public void passM1() {
final HelperClass helperClass = new HelperClass(new Data());
helperClass.verifyFlag();
}
@Test
public void failM2() {
final Data data = new Data();
data.setFlag(false);
final HelperClass helperClass = new HelperClass(data);
helperClass.verifyFlag();
}
}