我有UI自动化测试。测试涉及三个实体-
数据对象类-要在表单中填写的数据。在此,页面上的每个表单都可以由不同的数据对象表示
Helper类-在页面上的表单中填充数据测试类-使用数据对象和辅助类来执行测试。
以下是测试的精简版本-
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;
}
}
当并行执行方法时,我遇到了奇怪的结果,因为数据不是线程安全的。然后我合并了同步块,但我遇到了奇怪的结果。我肯定我搞砸了同步应该如何在这里使用。有什么见解吗?
我又做了一次运动。我设置了另一个与第一个测试类完全相同的测试类。我删除了助手和数据类中的所有同步。当我并行运行类(而不是方法)时。测试结果如预期。当我并行执行类时,即使它们使用相同的帮助程序类和数据对象,为什么我不运行并发呢?
HelperClass
和Data
是线程安全的。
问题是您的一些测试方法执行了几个操作。并且,只要测试方法中的操作序列不同步,它就不是原子的。
例如,在failM4
执行期间,helperClass
的状态可能会被其他线程修改。
我建议您不要在测试方法之间使用共享状态,因为同步会抵消并发测试执行的优势。
考虑使用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;
}
}
其他改进:
- CCD_ 7和CCD_
- 由于
HelperClass
需要一个Data
的实例才能工作,因此它应该使用构造函数依赖项声明它 使用时:
synchronized(this)
包装整个方法体,考虑在方法声明中使用
synchronized
关键字(更可读)。ThreadLocal
的不再需要同步
测试无状态
@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();
}
}