软件测试是评估和验证软件产品和应用程序是否达到预期效果的重要环节,通过测试能够避免一些bug,提升稳定性,验证软件功能是否完整,为用户提供更好的产品服务。
软件测试可分为:
功能性测试是根据产品需求来验证软件系统是否正常的一种软件测试,它的目的是测试软件中每一个功能是否达到预期效果。主要通过输入参数,根据功能需求来验证输出是否正确,它是一种黑盒测试,因为在测试中不涉及到源代码。
功能性测试对于验证软件系统的功能和质量至关重要,QA团队根据产品的需求,通过功能性测试来验证软件功能是否工作正常。
JUnit是在Java中使用最广泛的测试框架,可以通过以下方式引入
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
testImplementation 'com.google.truth:truth:1.1.3'
Truth是由Guava团队维护的一个代码库,Google内部的代码库也用它来进行测试。
val result = ValidatorUtils.validateRegistrationFields("", "123456", "123456")
assertThat(result).isFalse()
使用它可以让代码更加简洁,易于理解。下面写一个简单的单元测试来验证邮箱和密码是否合法。
在Android Studio项目app/src/test/java目录中创建ValidatorUtilsTest.kt
1class ValidatorUtilsTest {
2
3 @Test
4 fun `empty username returns false`() {
5 val result = ValidatorUtils.validateRegistrationFields("", "123456", "123456")
6 assertThat(result).isFalse()
7 }
8
9 @Test
10 fun `valid username and correctly repeated password returns true`() {
11 val result = ValidatorUtils.validateRegistrationFields("Eric", "123456", "123456")
12 assertThat(result).isTrue()
13 }
14
15 @Test
16 fun `username already exists returns false`() {
17 val result = ValidatorUtils.validateRegistrationFields("John", "123456", "123456")
18 assertThat(result).isFalse()
19 }
20
21 @Test
22 fun `empty password returns false`() {
23 val result = ValidatorUtils.validateRegistrationFields("John", "", "")
24 assertThat(result).isFalse()
25 }
26
27 @Test
28 fun `incorrectly repeated password returns false`() {
29 val result = ValidatorUtils.validateRegistrationFields("John", "123456", "abcdef")
30 assertThat(result).isFalse()
31 }
32
33 @Test
34 fun `less than two digits password returns false`() {
35 val result = ValidatorUtils.validateRegistrationFields("John", "abcde1", "abcde1")
36 assertThat(result).isFalse()
37 }
38}
除了在单元测试中,请不要使用‘xxxx’定义方法名
1object ValidatorUtils {
2
3 val existingUsernames = listOf("John", "david", "nick")
4
5 fun validateRegistrationFields(username: String, password: String, confirmedPassword: String): Boolean {
6 if (username.isEmpty() || password.isEmpty()) {
7 return false
8 }
9 if (username in existingUsernames) {
10 return false
11 }
12 if (password != confirmedPassword) {
13 return false
14 }
15 if (password.count { it.isDigit() } < 2) {
16 return false
17 }
18 return true
19 }
20}
Mockito是一个非常流行的开源软件测试框架,它极大的简化了对具有外部依赖的类的测试,它能够模拟依赖的类或接口。
通过mock静态方法或@Mock注解来创建模拟对象。when(….).thenReturn(….)
链式调用可以根据指定的参数来返回不同的值。下面写一个针对firebase方法的测试用例。
首先在build.gradle中添加Mockito的依赖
implementation "org.mockito:mockito-core:3.5.13"
implementation "org.mockito:mockito-inline:3.5.13"
然后编写测试类
1@RunWith(MockitoJUnitRunner::class)
2class MainActivityViewModelTest {
3
4 private var firebaseAuth = mock(FirebaseAuth::class.java)
5
6 @Mock
7 private lateinit var taskMock: Task<AuthResult>
8
9 private val viewModel: MainActivityViewModel by lazy {
10 MainActivityViewModel(
11 UserRepositoryImpl(
12 firebaseFirestore,
13 firebaseAuth)
14 )
15 }
16
17 @Test
18 fun testUserLogin() {
19 val email = "[email protected]"
20 val password = "123456"
21
22 `when`(firebaseAuth.signInWithEmailAndPassword(email, password)).thenReturn(taskMock)
23 viewModel.userLogin(email, password)
24
25 verify(firebaseAuth).signInWithEmailAndPassword(email, password)
26 verify(taskMock).addOnCompleteListener(any(MyOnCompleteListener::class.java))
27 }
28}
29
30interface UserRepository {
31 fun login(email: String, password: String)
32}
33
34class UserRepositoryImpl @Inject constructor(
35 private val firebaseAuth: FirebaseAuth
36): UserRepository {
37
38 override fun login(email: String, password: String) {
39 firebaseAuth.signInWithEmailAndPassword(email, password)
40 .addOnCompleteListener(object : MyOnCompleteListener(this) {
41 override fun onComplete(p0: Task<AuthResult>) {
42 println("!@# onComplete called")
43 }
44 })
45 }
46}
47
48@HiltViewModel
49class MainActivityViewModel @Inject constructor(
50 private val userRepository: UserRepository
51) : BaseViewModel() {
52
53 fun userLogin(email: String, password: String) {
54 userRepository.login(email, password)
55 }
56}
57
58open class MyOnCompleteListener(): OnCompleteListener<AuthResult> {
59 private lateinit var callBackListener: CallBackListener
60
61 constructor(
62 callBackListener: CallBackListener
63 ): this() {
64 this.callBackListener = callBackListener
65 }
66
67 override fun onComplete(p0: Task<AuthResult>) {
68 callBackListener.doSomething()
69 }
70}