如何应用同步



我有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;
        }
    }

当并行执行方法时,我遇到了奇怪的结果,因为数据不是线程安全的。然后我合并了同步块,但我遇到了奇怪的结果。我肯定我搞砸了同步应该如何在这里使用。有什么见解吗?

我又做了一次运动。我设置了另一个与第一个测试类完全相同的测试类。我删除了助手和数据类中的所有同步。当我并行运行类(而不是方法)时。测试结果如预期。当我并行执行类时,即使它们使用相同的帮助程序类和数据对象,为什么我不运行并发呢?

HelperClassData是线程安全的。

问题是您的一些测试方法执行了几个操作。并且,只要测试方法中的操作序列不同步,它就不是原子的。

例如,在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();
    }
}

最新更新