异步过程的自动化测试

自从我开发了具有异步行为的服务器端应用程序以来已经有一段时间了,该行为还不是事件驱动的系统。 异步行为始终是设计和测试中一个有趣的挑战。 通常,异步行为不应该很难进行单元测试-毕竟,动作的行为不一定必须在时间上进行耦合(请参见耦合形式 )。

提示:如果在单元测试中发现需要异步测试,则可能是做错了,需要重新设计代码以消除这些顾虑。

如果您的测试策略仅包括单元测试,您将错过一整套行为,而这些行为通常会在集成,功能或系统测试等高级测试中被发现,而这正是我需要异步测试的地方。

从概念上讲,异步测试实际上非常容易。 像同步测试一样,您需要采取措施,然后寻找所需的结果。 但是,与同步测试不同,测试不能保证在检查副作用或结果之前动作已经完成。

通常有两种方法来测试异步行为:

  1. 删除异步行为
  2. 轮询直到您具有所需的状态

删除异步行为

多年前,当TDD对胖客户端应用程序进行TDD时,我仍然使用这种方法,而在swing应用程序中编写应用程序仍然是一种常见的方法。 这样做需要将调用行为的行为隔离在一个地方,而不是在不同的线程中发生,而是在测试过程中发生在与测试相同的线程中。 我什至在2006年对此进行了介绍,并撰写了这份备忘单,介绍了该过程。

这种方法需要一种纪律性的设计方法,在这种情况下,将这种行为切换到一个单独的位置即可。

轮询直到您具有所需的状态

轮询是解决此问题的更为常见的方法,但是这涉及到等待和超时的常见问题。 等待时间过长会增加您的总体测试时间,并会延长反馈循环。 根据您的操作,等待时间太短可能也很昂贵(例如,不必要地锤击某些积分点)。

超时是异步行为的另一种诅咒,因为您实际上不知道何时要执行某项操作,但您实际上并不希望测试永远进行下去。

上一次我不得不做某事时,我们通常会最终编写自己的轮询和超时挂钩,而相对简单的现在可以作为非常简单的库使用。 幸运的是,其他人也在java-land遇到了这个问题,并提供了一个库来帮助简化Awaitility形式的测试

这是一个简单的测试,演示了该库使测试异步行为的容易程度:

package com.thekua.spikes.aysnc.testing;

import com.thekua.spikes.aysnc.testing.FileGenerator;
import org.junit.Before;
import org.junit.Test;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import static java.util.concurrent.TimeUnit.SECONDS;
import static org.awaitility.Awaitility.await;
import static org.hamcrest.Matchers.startsWith;
import static org.junit.Assert.assertThat;

public class FileGeneratorTest {

    private static final String RESULT_FILE = "target/test/resultFile.txt";
    private static final String STEP_1_LOG = "target/test/step1.log";
    private static final String STEP_2_LOG = "target/test/step2.log";
    private static final String STEP_3_LOG = "target/test/step3.log";

    private static final List<String> FILES_TO_CLEAN_UP = Arrays.asList(STEP_1_LOG, STEP_2_LOG, STEP_3_LOG, RESULT_FILE);


    @Before
    public void setUp() {
        for (String fileToCleanUp : FILES_TO_CLEAN_UP) {
            File file = new File(fileToCleanUp);
            if (file.exists()) {
                file.delete();
            }
        }
    }


    @Test
    public void shouldWaitForAFileToBeCreated() throws Exception {
        // Given I have an aysnc process to run
        String expectedFile = RESULT_FILE;

        List<FileGenerator> fileGenerators = Arrays.asList(
                new FileGenerator(STEP_1_LOG, 1, "Step 1 is complete"),
                new FileGenerator(STEP_2_LOG, 3, "Step 2 is complete"),
                new FileGenerator(STEP_3_LOG, 4, "Step 3 is complete"),
                new FileGenerator(expectedFile, 7, "Process is now complete")
        );

        // when it is busy doing its work
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        for (final FileGenerator fileGenerator : fileGenerators) {
            executorService.execute(new Runnable() {
                public void run() {
                    fileGenerator.generate();
                }
            });
        }

        // then I get some log outputs
        await().atMost(2, SECONDS).until(testFileFound(STEP_1_LOG));
        await().until(testFileFound(STEP_2_LOG));
        await().until(testFileFound(STEP_3_LOG));

        // and I should have my final result with the output I expect
        await().atMost(10, SECONDS).until(testFileFound(expectedFile));
        String fileContents = readFile(expectedFile);
        assertThat(fileContents, startsWith("Process"));

        // Cleanup
        executorService.shutdown();
    }

    private String readFile(String expectedFile) throws IOException {
        return new String(Files.readAllBytes(Paths.get(expectedFile)));

    }


    private Callable<Boolean> testFileFound(final String file) {
        return new Callable<Boolean>() {
            public Boolean call() throws Exception {
                return new File(file).exists();
            }
        };
    }
}

您可以在此公共git存储库上浏览完整的演示代码。

翻译自: https://www.javacodegeeks.com/2017/04/automated-tests-asynchronous-processes.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值