如何编写并发单元测试

时间:2016-12-20 10:47:40

标签: java junit

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

2 个答案:

答案 0 :(得分:2)

对我来说,使用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");
}

答案 1 :(得分:0)

现在,我正在用Kotlin编写所有测试。通过Kotlin测试,这既轻松又有趣!

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

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