记录junit 测试框架使用

springboot 测试类

  1. 测试基类配置
@RunWith(SpringRunner.class)
@SpringBootTest
@WebAppConfiguration
@ContextConfiguration(initializers=ConfigFileApplicationContextInitializer.class)
@Slf4j
public class BreakfastApplicationTests {
}

springboot 测试类编写时 可以通过申明一个基类 同时添加@SpringBootTest与@RunWith注解, 如果需要访问数据库,则需要在代码中申明 dataSource

自定义数据源

Ps: 如果测试需要单独的dataDource 则可以在基类中自行配置数据源

不使用单独数据源

  1. 测试类编写
@Slf4j
@ActiveProfiles("dev")
@TestPropertySource("classpath:application-dev.yml")
public class WalletServiceTest extends BreakfastApplicationTests {
}

通过@ActiveProfiles 指定对应的环境, 通过@TestPropertySource 指定需要加载的配置文件

解决JUNIT测试框架不支持多线程测试

  1. 使用thread.join
@Test
public void updateTest() {
    for (int i = 0; i < 30; i++) {
        Runnable run = new Runnable() {
            public void run() {
                WalletDto walletDto = new WalletDto();
                walletDto.setTotalBalance(new BigDecimal(0.5).multiply(new BigDecimal(new Random().nextInt(100))));
                walletDto.setFrozenAmount(new BigDecimal(0.5).multiply(new BigDecimal(new Random().nextInt(50))));
                walletDto.setPassword("11111111");
                walletDto.setPwdSalt("dhxh");
                walletDto.setCreateTime(new Date(System.currentTimeMillis()));
                walletDto.setDeleteFlag(Boolean.FALSE);
                walletDto.setId(3);
                walletDto.setOperator(2);
                walletDto.setStoreId(1);
                walletDto.setUpdateTime(new Date(System.currentTimeMillis()));
                walletDto.setDeleteTime(new Date(System.currentTimeMillis()));
                walletService.update(walletDto);
            }
        };
        
        /* 使用线程池 如果没有阻塞主线程,junit在主线程执行完后,会调用System.exit() 方法, 停止JVM*/
        executorConfig.asyncServiceExecutor().execute(run);

        /* 不使用线程池 */
        Thread thread = new Thread(run);
        thread.start();
        try {
            /*加入主线程 或thread.sleep(太low)*/
            thread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
  1. 使用countDownLatch
/**
 * 一种简单的方法是使用CountDownLatch.
 *
 * 使用CountDownLatch latch = new CountDownLatch(30)初始化CountDownLatch.
 * 传递每个计算器可运行对锁存器的引用.在run()方法结束时,调用latch.countDown().
 * 在方法结束时调用latch.await().这将阻塞,直到latch.countDown()被调用30次(即所有线程完成时).
 */
@Test
public void update() {
    CountDownLatch countDownLatch = new CountDownLatch(30);
    for (int i = 0; i <= 30; i++) {
        executorConfig.asyncServiceExecutor().execute(() -> {
            WalletDto walletDto = new WalletDto();
            walletDto.setTotalBalance(new BigDecimal(0.5).multiply(new BigDecimal(new Random().nextInt(100))));
            walletDto.setFrozenAmount(new BigDecimal(0.5).multiply(new BigDecimal(new Random().nextInt(50))));
            walletDto.setPassword("11111111");
            walletDto.setPwdSalt("dhxh");
            walletDto.setCreateTime(new Date(System.currentTimeMillis()));
            walletDto.setDeleteFlag(Boolean.FALSE);
            walletDto.setId(3);
            walletDto.setOperator(2);
            walletDto.setStoreId(1);
            walletDto.setUpdateTime(new Date(System.currentTimeMillis()));
            walletDto.setDeleteTime(new Date(System.currentTimeMillis()));
            log.info("walletDto = {}", walletDto);
            walletService.update(walletDto);
            countDownLatch.countDown();
            log.info("count = {}", countDownLatch.getCount());
        });
    }
    try {
        countDownLatch.await(1, TimeUnit.MINUTES);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

Ps: 如果没有阻塞主线程, junit会在主线程执行完之后 调用System.exit(), 即使子线程没有执行完。

  1. 其实junit是将test作为参数传递给了TestRunner的main函数。并通过main函数进行执行。test函数在main中执行。如果test执行结束,那么main将会调用System.exit(0);
  2. 即使还有其他的线程在运行,main也会调用System.exit(0);
  3. System.exit()是系统调用,通知系统立即结束jvm的运行,即使jvm中有线程在运行,jvm也会停止的。所以会出现之前的那种情况。其中System.exit(0);的参数如果是0,表示系统正常退出,如果是非0,表示系统异常退出。

未阻塞主线程

未阻塞主线程,退出日志不足30条


TestRunner部分源码

public static void main(String[] args) {
        TestRunner aTestRunner = new TestRunner();

        try {
            TestResult r = aTestRunner.start(args);
            if (!r.wasSuccessful()) {
                System.exit(1);
            }

            System.exit(0);
        } catch (Exception var3) {
            System.err.println(var3.getMessage());
            System.exit(2);
        }

    }