Java单元测试是否应该全程使用相同的公共变量?
JUnit 单元测试公共资源初始化最佳方案
首先明确:JUnit 默认的测试实例生命周期下,你担心的多线程并发测试变量篡改问题不会出现。
- JUnit4、JUnit5 默认都会为每一个测试方法单独创建一个测试类的全新实例,
@Before(JUnit4)/@BeforeEach(JUnit5)注解的方法会在每个测试方法执行前都运行一次,你在这个方法里给类成员变量赋值,每个测试方法拿到的都是属于自己实例的独立变量,就算多个测试方法并发执行,也不会互相修改对方的变量值。 - 只有当你主动修改了测试实例生命周期(比如JUnit5加了
@TestInstance(Lifecycle.PER_CLASS)注解,整个测试类只生成一个实例),或者用了静态成员变量的时候,才会出现多线程跑测试时变量被篡改的问题,这种场景要避免修改公共资源,或者给资源加线程隔离。
场景1:资源初始化成本低(比如普通列表、简单变量)
直接用@Before/@BeforeEach初始化类成员变量即可,不需要额外处理,这是最常用也最稳妥的方案,既能减少重复代码,也完全没有并发安全问题。
示例代码:
public class MyListTest { private List<String> testList; @Before public void setup() { // 每次测试前都生成全新的列表,各测试方法之间完全隔离 testList = new ArrayList<>(Arrays.asList("a", "b", "c")); } @Test public void testAdd() { testList.add("d"); Assert.assertEquals(4, testList.size()); } @Test public void testRemove() { testList.remove(0); Assert.assertEquals(2, testList.size()); } }
场景2:资源初始化成本很高(比如数据库连接、大对象加载)
如果每次测试都重新创建资源开销太大,你可以把资源声明为静态变量,用@BeforeClass(JUnit4)/@BeforeAll(JUnit5)注解的方法只初始化一次,但是要注意:
- 所有测试方法都不能修改这个公共资源,保证只读
- 如果必须要修改,可以用
ThreadLocal做线程隔离,或者每次修改前对资源做深拷贝
要尽量避免的错误做法:不要在测试类里定义可变的静态变量又不做隔离,很容易出现测试结果不稳定的 flaky 测试。
内容的提问来源于stack exchange,提问作者user17186249
相关产品推荐
相关产品推荐

