繁体   English   中英

如何编写并发单元测试

[英]How to write concurrent unit tests

Awaitility是对并发生产代码进行单元测试的好工具。

问题:有没有工具可以简化并发测试代码的编写?

假设我想测试java.util.concurrent.LinkedBlockingQueue

public class BlockingQueueTest {
    private LinkedBlockingQueue<String> out;

    @Before
    public void setUp() {
        out = new LinkedBlockingQueue<>();
    }

    @Test
    public void putThenGet() throws InterruptedException {
        // that's easy because it can be done in one thread
        out.put("Hello");

        String taken = out.take();

        assertThat(taken).isEqualTo("Hello");
    }

    @Test
    public void getBeforePut() throws InterruptedException {
        // that's more tricky because it can't be done with one thread
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        executorService.submit(() -> {
            Thread.sleep(100);
            out.put("Hello");
            return null;
        });
        executorService.shutdown();

        String taken = out.take();

        assertThat(taken).isEqualTo("Hello");
    }
}

getBeforePut()编写代码并不有趣。 有没有办法让它变得不那么难读,像这样?

@Test
public void getBeforePut2() throws InterruptedException {
    // Wanted: DSL for concurrent test-code
    Concurrently.sleep(100, TimeUnit.MILLISECONDS).andThen(() -> out.put("Hello"));

    String taken = out.take();

    assertThat(taken).isEqualTo("Hello");
}

对我来说,使用TestNG是最简单的方法:

 @Test(threadPoolSize = 10, invocationCount = 15, timeOut = 1000)
 public void testPut(){
     out.put("Hello");
 }

这个测试将在 10 个线程中运行 15 次,并且应该不会超过 1000 毫秒。

您还可以创建依赖于其他测试的测试

@Test(dependsOnMethods = "testPut")
public void testGetAfterPut{
    String taken = out.take();

    assertThat(taken).isEqualTo("Hello");
}

(A) 你可以只使用 Thread 而不需要 ExecutorService

@Test
public void getBeforePutWithThread() throws InterruptedException {
    new Thread(() -> {
        Thread.sleep(100);
        out.put("Hello");
    }).run();

    String taken = out.take();

    assertThat(taken).isEqualTo("Hello");
}

(B) 你可以把所有的功能都变成一个简单的函数,避免依赖外部库

private void runWithDelay(long delay, Runnable action) {
    new Thread(() -> {
        try {
            Thread.sleep(delay);
            action.run();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }).run();     
}

@Test
public void getBeforePutWithFunction() {
    runWithDelay(100, () -> out.put("Hello"));

    String taken = out.take();

    assertThat(taken).isEqualTo("Hello");
}

到目前为止,我正在用 kotlin 编写所有测试。 并且通过 kotlin 测试,这很容易而且很有趣!

使用线程进行测试时值得一提的是JUnit 的 @Timeout 注解,它可以防止错误测试无限运行。

import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.Timeout
import java.util.concurrent.LinkedBlockingQueue
import kotlin.concurrent.thread

class BlockingQueueKotlinTests {
    // objectUnderTest
    private val out = LinkedBlockingQueue<String>()

    @Test
    fun `put then get`() {
        // that's easy because it can be done in one thread
        out.put("Hello")

        val taken = out.take()

        assertThat(taken).isEqualTo("Hello")
    }

    @Test
    @Timeout(1)
    fun `get before put`() {
        // thanks to kotlin it's really easy to do that in another thread
        thread {
            Thread.sleep(100)
            out.put("kotlin is great!")
        }

        val taken = out.take()

        assertThat(taken).isEqualTo("kotlin is great!")
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM