JUnit 4 → JUnit 5 + Kotlin
老写法(Java + JUnit 4)
java
@RunWith(MockitoJUnitRunner.class)
public class ItemViewModelTest {
@Mock ApiService apiService;
@Captor ArgumentCaptor<String> captor;
private ItemViewModel viewModel;
@Before
public void setUp() {
viewModel = new ItemViewModel(apiService);
}
@Test
public void loadItems_success() {
List<Item> items = Arrays.asList(new Item(1, "test"));
when(apiService.loadItems()).thenReturn(items);
viewModel.loadItems();
verify(apiService).loadItems();
assertEquals(1, viewModel.getItems().getValue().size());
}
@Test(expected = IOException.class)
public void loadItems_error() throws Exception {
when(apiService.loadItems()).thenThrow(new IOException());
viewModel.loadItems();
}
}
问题在哪里
@RunWith 只能用在一个 Test 类上,无法组合多个 Runner。@Test(expected = ...) 无法精确验证异常信息,且只能检查方法结束时是否抛了异常,中间抛了也算通过。
新写法(JUnit 5 + Kotlin)
kotlin
@ExtendWith(MockKExtension::class)
class ItemViewModelTest {
@MockK lateinit var apiService: ApiService
private lateinit var viewModel: ItemViewModel
@BeforeEach
fun setUp() {
viewModel = ItemViewModel(apiService)
}
@Test
fun `load items returns data on success`() = runTest {
val items = listOf(Item(1, "test"))
coEvery { apiService.loadItems() } returns items
viewModel.loadItems()
coVerify { apiService.loadItems() }
assertEquals(1, viewModel.items.value?.size)
}
@Test
fun `load items handles error`() = runTest {
coEvery { apiService.loadItems() } throws IOException("网络错误")
assertThrows<IOException> {
runBlocking { viewModel.loadItems() }
}
}
}
一句话注意
JUnit 5 的 @ExtendWith 可以组合多个 Extension(相当于 JUnit 4 的多个 Runner 组合)。Kotlin 中测试方法名可以用反引号括起来写中文或完整的英文描述,比 Java 的 camelCase 方法名可读性高。
runTest 是 kotlinx-coroutines-test 提供的测试协程作用域,自动跳过 delay 等挂起时间,测试不会真的等待。
Java Android 老项目迁移系列,持续更新中。