jck28-lucio-junit5并行数据同步

Synchronization

  • 共享资源的同步
  • JUnit5@ResourceLock注解的形式为我们提供了这样的机制。

串行用例

  • 可以看到单线程的时候每次测试用例断言都通过,说明可以正确的拿到对应的值。

并行用例

  • 如果换成多线程的时候,可以看到代码不稳定性,有时断言通过,有时断言失败。
  • 这个时候如果想要进行对应的代码健壮性应该怎样修改呢??

@ResourceLock

  • @ResourceLock相当于Java代码中的 synchronized@Synchronized
  • @ResourceLock注解为 测试类测试方法 提供声明式同步机制。
  • @ResourceLock注解有两个参数
    • 一个是String指定唯一标识共享资源的值
      • 资源值可以是 预定义 的或 用户定义
    • 一个是ResourceAccessMode指定访问资源的模式
  • 访问模式可以是 READ 「只读」和 READ_WRITE「读和写」

多线程报错

package com.junit5.synch;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.parallel.*;

import java.util.Properties;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;

@Execution(ExecutionMode.CONCURRENT)
public class ParallelResourceLockTest {

    Properties properties;

    @BeforeEach
    void before(){
        properties= new Properties(System.getProperties());
    }

    @Test
    void test01(){
        assertNull(System.getProperty("custom.property"));
    }

    @Test
    void test02(){
        System.setProperty("custom.property","juni5");
        assertEquals("juni5",System.getProperty("custom.property"));
    }

    @Test
    void test03(){
        System.setProperty("custom.property","hogwarts");
        assertEquals("hogwarts",System.getProperty("custom.property"));
    }
}

加锁

package com.junit5.synch;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.parallel.*;

import java.util.Properties;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;

@Execution(ExecutionMode.CONCURRENT)
public class ParallelResourceLockTest {

    Properties properties;

    @BeforeEach
    void before(){
        properties= new Properties(System.getProperties());
    }

    @Test
    @ResourceLock(value = Resources.SYSTEM_PROPERTIES,mode = ResourceAccessMode.READ)
    void test01(){
        assertNull(System.getProperty("custom.property"));
    }

    @Test
    @ResourceLock(value = Resources.SYSTEM_PROPERTIES,mode = ResourceAccessMode.READ_WRITE)
    void test02(){
        System.setProperty("custom.property","juni5");
        assertEquals("juni5",System.getProperty("custom.property"));
    }

    @Test
    @ResourceLock(value = Resources.SYSTEM_PROPERTIES,mode = ResourceAccessMode.READ_WRITE)
    void test03(){
        System.setProperty("custom.property","hogwarts");
        assertEquals("hogwarts",System.getProperty("custom.property"));
    }
}

预定义资源

  • Resources.SYSTEM_PROPERTIES
    • 表示 Java 的系统属性。
  • Resources.SYSTEM_OUT
    • 代表当前进程的标准输出流。
  • Resources.SYSTEM_ERR
    • 表示当前进程的标准错误流。
  • Resources.LOCALE
    • 当前 JVM 实例的默认语言环境。
  • Resources.TIMEZONE
    • 当前 JVM 实例的默认时区。

自定义资源

  • 全局用户
  • 如果是SAME_THREAD,运行测试用例的断言结果都是正常,但是当使用了并发执行,对应的用例就会报错。
  • 解决:
    • 在对应测试方法上添加相关的 @ResourceLock

自定义资源

  • 1.自定义类的全限定类名为value
  • 2.如果是没有值的写入,对应 modeREAD「只可读」;
    • 如果有内容写入自定义类,对应modeREAD_WRITE「既可读又可写」
package com.junit5;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

public class User {
    static Map<Integer,String> global_user = new HashMap<>();

    public static String get(int id){
        return global_user.get(id);
    }

    public static void add(int id,String user){
        global_user.put(id,user);
    }

    public static void update(int id,String user){
        global_user.put(id,user);
    }

    public static void remove(int id){
        global_user.remove(id);
    }

    public static void clear(){
        global_user.clear();
    }

    public static Collection<String> getUser(){
        return global_user.values();
    }
}
package com.junit5.synch;

import com.junit5.User;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.parallel.*;

import java.util.Properties;

import static org.junit.jupiter.api.Assertions.*;

@Execution(ExecutionMode.CONCURRENT)
public class ParallelResourceLock02Test {

    public static final String GOLOBAL_USER = "com.junit5.User.user";

    @BeforeEach
    void before(){
        User.clear();
    }

    @Test
    @ResourceLock(value = GOLOBAL_USER,mode = ResourceAccessMode.READ)
    void test01(){
        System.out.println("test01==>"+User.getUser());
       assertTrue(User.getUser().isEmpty());
    }

    @Test
    @ResourceLock(value = GOLOBAL_USER,mode = ResourceAccessMode.READ_WRITE)
    void test02(){
      User.add(1,"gaoyuanyuan");
        System.out.println("test02==>"+User.getUser());
        assertEquals("gaoyuanyuan",User.get(1));
    }

    @Test
    @ResourceLock(value = GOLOBAL_USER,mode = ResourceAccessMode.READ_WRITE)
    void test03(){
        User.update(1,"liushishi");
        System.out.println("test03==>"+User.getUser());
        assertEquals("liushishi",User.get(1));
    }

    @Test
    @ResourceLock(value = GOLOBAL_USER,mode = ResourceAccessMode.READ_WRITE)
    void test04(){
        User.add(2,"guanzhiling");
        System.out.println("test04==>"+User.getUser());
        User.remove(2);
        System.out.println("test02==>remove==>"+User.getUser());
        assertNull(User.get(2));
    }

}

总结

并行测试对应共享数据可以通过 @ResourceLock 来进行数据的同步