目录
一、前言
JUnit和Mockito这两个单元测试框架在Java工程中被广泛应用。
但到2021年,仍有很多开发者在使用JUnit4+Mockito2+PowerMock,但不得不说,这套配置已经开始过时。
如今,JUnit5已经迭代到5.8.x,Mockito也进入了4.x阶段,开发者可以开始基于JUnit5+Mockito3来进行单测的开发。由于PowerMock在JUnit5上无法工作,所以不再使用PowerMock。Mockito3.4后,已经支持mock静态方法(Mockito (Mockito 3.5.10 API) (javadoc.io)),不引入PowerMock也能继续单测开发。
================================2023.04更新================================
Mockito已经更新至5.0,最低要求JDK11,带来了一些列新特性的支持与性能提升。PowerMock于2022年merge了一个支持Mockito4的PR(mockito 4.3.1 by sullis · Pull Request #1113 · powermock/powermock (github.com)),但截止2023.04,PowerMock仍然没有发布新的release版本。
如果应用基于JDK11,可以考虑使用Mockito5。
二、Maven依赖
2.1 JUNIT5
首先引入JUNIT5。
<!-- junit5 -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.8.2</version>
<scope>test</scope>
</dependency>
2.2 Mockito
2.2.1 Mockito3
除了引入mockito-core外,还需要引入mockito-jupiter扩展包。
其中mockito-inline是对Mockito的增强,包括mock final类 static方法。
<!-- mockito3 -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.12.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<version>3.6.28</version>
<scope>test</scope>
</dependency>
<!-- mockito for junit5 -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>2.23.0</version>
<scope>test</scope>
</dependency>
2.2.2 Mockito5
<!-- 5.0后无需引入mockito-inline -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>5.2.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>5.2.0</version>
<scope>test</scope>
</dependency>
2.3 surefire
为了单测能正常运行并被mvn test识别,还需要更新maven-surefire-plugin,添加相关依赖。
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0</version>
<configuration>
<testFailureIgnore>true</testFailureIgnore>
</configuration>
<!-- 下面依赖视情况添加,如果单测可以运行则不需要加 -->
<dependencies>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-surefire-provider</artifactId>
<version>1.3.2</version>
</dependency>
</dependencies>
</plugin>
三、UT示例
业务代码:
@Service
public class DomainServiceImpl implements DomainService {
@Resource
private DataJpa dataJpa;
@Override
public Boolean foo(List<String> names) {
int res = dataJpa.update(names);
return res == 1;
}
}
单元测试:
在JUNIT5中,不再提供@RunWith注解,需要使用@ExtendWith注解。
为了让Mockito正常工作,需要在@ExtendWith注解中使用MockitoExtension.class
同时,@Before被@BeforeAll或者@BeforeEach替换,若使用@BeforeAll,需要写在静态方法上。
@ExtendWith(MockitoExtension.class)
class DomainServiceImplTest {
@InjectMocks
private DomainServiceImpl domainService;
@Mock
private DataJpa dataJpa;
@BeforeAll
static void setUp() {
}
@Test
void foo() {
List<String> names = Lists.newArrayList("a", "b");
Mockito.when(dataJpa.update(Mockito.anyList())).thenReturn(1);
boolean res = domainService.foo(names);
Assertions.assertTrue(res);
}
}
静态方法Mock:
// code:
class Foo {
public static String say() {
return "hello";
}
}
// ut:
Assertions.assertEquals("hello", Foo.say());
try (MockedStatic<Foo> mocked = Mockito.mockStatic(Foo.class)) {
mocked.when(Foo::say).thenReturn("world");
String res = Foo.say();
Assertions.assertEquals("world", res);
}
// JDK11及以上版本中,try块中的变量可以在外部声明
MockedStatic<Foo> mocked = Mockito.mockStatic(Foo.class);
try (mocked) {
mocked.when(Foo::say).thenReturn("world");
String res = Foo.say();
Assertions.assertEquals("world", res);
}