laitimes

Software Testing Learning Notes丨JUnit5 Parallel Data Synchronization

author:Tested 666
This article is transferred from the tester community, the original link: jck28-lucio-junit5 parallel data synchronization - study notes - tester community

Synchronization

  • Synchronization of shared resources
  • JUnit5 provides us with such a mechanism in the form of @ResourceLock annotations.

Serial use cases

  • It can be seen that when a single thread is used, the assertion of each test case is passed, indicating that the corresponding value can be obtained correctly.

Parallel use cases

  • If you switch to multithreading, you can see that the code is unstable, sometimes assertion passes, sometimes assertions fail.
  • At this time, if you want to make the corresponding code robustness, how should you modify it?

@ResourceLock

  • @ResourceLock equivalent to synchronized and @Synchronized in Java code
  • @ResourceLock annotations provide a declarative synchronization mechanism for test classes and test methods.
  • @ResourceLock annotation has two parameters
    • One is that the String specifies a value that uniquely identifies the shared resource
    • Resource values can be predefined or user-defined
      • One is ResourceAccessMode, which specifies the mode in which resources are accessed
  • Access modes can be READ and READ_WRITE Read & Write

Multi-threading errors

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"));
    }
}           

Locking

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"));
    }
}           

Predefined resources

  • Resources.SYSTEM_PROPERTIES
    • Represents the system properties of Java.
  • Resources.SYSTEM_OUT
    • A standard output stream that represents the current process.
  • Resources.SYSTEM_ERR
    • Represents the standard error flow of the current process.
  • Resources.LOCALE
    • The default locale for the current JVM instance.
  • Resources.TIMEZONE
    • The default time zone for the current JVM instance.

Custom resources

  • Global users
  • If it is SAME_THREAD, the assertion result of running the test case is normal, but when concurrent execution is used, the corresponding case will report an error.
  • Settle:
    • Add the relevant @ResourceLock on the corresponding test method

Custom resources

  • 1. The fully qualified class name of the custom class is value
  • 2. If it is a write without a value, the corresponding mode is READ "read-only";
    • If something is written to a custom class, the corresponding mode is READ_WRITE "both readable and writable"
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));
    }

}           

summary

Parallel test corresponding to shared data can be synchronized through @ResourceLock

Free video tutorial sharing for software testing and development - Official Account - Tester Community

Software Testing Learning Notes丨JUnit5 Parallel Data Synchronization

Read on