android 逐帧动画_android逐案测试

薛经艺
2023-12-01

android 逐帧动画

Table of contents

目录

· Best practices
·
Some scenarios for writing test cases
Simple unit test for class
Testing callback function
Testing suspend function
Testing coroutine when launch in Dispatchers.Main
Testing Dagger (Replace module)
Testing with mockwebserver
Unit Test that need Application/Activity
Test with Room

·最佳做法·编写测试用例的一些方案类简单的单元测试测试回调函数测试暂停功能out在Dispatchers中启动时测试协程测试匕首(替换模块) 测试与mockwebserver 需要应用/活动的单元测试测试客房

最佳实践(Best practices)

  • Test case should have 3 separated parts: GIVEN — WHEN — THEN

    测试用例应包含3个单独的部分:给定—何时—然后

  • Use prefix actual_ and expected_ for 2 comparable params in assert

    使用前缀actual_expected_在断言2个可比PARAMS

  • Should hardcode input to make test case understandable

    应该对输入进行硬编码以使测试用例易于理解
  • Should not make a big test case with many assert → Separate to some small test cases with specific purpose

    不应使用许多断言来构成一个大型测试用例→将某些特定目的的小型测试用例分开
  • Don’t make test case extends from each other. Instead we should make component and make it run before test

    不要使测试用例相互扩展。 相反,我们应该制作组件并使其在测试之前运行
  • Test case should not include logic. Make it as simple as possible

    测试用例不应包含逻辑。 使其尽可能简单
  • Focus on integration test instead of mock-based tests

    专注于集成测试,而不是基于模拟的测试
  • Should mock value that may change every time test run (such as Time)

    应该模拟可能在每次测试运行时都会更改的值(例如时间)

编写测试用例的一些方案 (Some scenarios for writing test cases)

上课的简单单元测试(Simple unit test for class)

If class has no dependencies so testing is just simple. For example, we want to test function parseDate function of DateUtils class, just create this class, call function and assert the result.

如果class没有依赖关系,那么测试就很简单。 例如,我们要测试DateUtils类的函数parseDate函数,只需创建此类,调用函数并声明结果即可。

@Test
public void parseDate_dateWrongFormat_returnNull() {
Date expectedDate = DateUtils().parseDate("10/13/98", "MM-dd-yy");
assertNull(expectedDate);
}

测试回调函数 (Testing callback function)

For testing async function that uses callback, we can use concurrentunit to pause thread to wait for callback is called.

为了测试使用回调的异步函数,我们可以使用current并发单元 暂停线程以等待回调。

First add library:

首先添加库:

testImplementation 'net.jodah:concurrentunit:0.4.6'

Use Waiter class to block and resume the main test thread, like this:

使用Waiter类阻止和恢复主测试线程,如下所示:

@Test
public void shouldDeliverMessage() throws Throwable {
final Waiter waiter = new Waiter();

messageBus.registerHandler(message -> {
// Called on separate thread
waiter.assertEquals(message, "foo");
waiter.resume();
};

// Wait for resume() to be called
waiter.await(1000);
}

测试暂停功能 (Testing suspend function)

First, add the librarykotlinx-coroutines-test

首先,添加库kotlinx-coroutines-test

testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutinesVersion"

Write test case with runBlockingTest to call suspend function

使用runBlockingTest编写测试用例以调用暂停功能

class Repository() {
suspend fun getData(): String {
delay(1000)
return "Result"
}
}@Test
fun testFunction() = runBlockingTest {// Suspend function calling here
val expectedResult = Repository().getData()

// Assert
assertTrue("Result", expectedResult)
}

Sometimes, it raises error “This job has not completed yet”. Add this to test class:

有时,它会引发错误“此作业尚未完成”。 将此添加到测试类:

@get:Rule
var instantExecutorRule = InstantTaskExecutorRule()

在Dispatchers.Main中启动时测试协程 (Testing coroutine when launch in Dispatchers.Main)

Check this code:

检查此代码:

class ArticleViewModel : ViewModel() {
private var likeCount = 0

fun getLikeCount() = likeCount

fun addLikeCount() {
viewModelScope.launch {likeCount += 1
likeCount
}}
}

class ArticleViewModelTest {
@Test
fun addLikeCount() = runBlocking {
val
articleViewModel = ArticleViewModel()
articleViewModel.addLikeCount()
Assert.assertEquals(1, articleViewModel.getLikeCount())
}}

If we have test case for coroutine code that is launched in Dispatchers.Main. The test will fail because the Dispatchers.Main uses Android main looper which is not available during unit tests. That’s available in instrumentation tests but not in unit tests.

如果我们有在Dispatchers.Main启动的协程代码测试用例。 测试将失败,因为Dispatchers.Main使用的是Android主循环程序,在单元测试期间不可用。 在仪器测试中可用,但在单元测试中不可用。

We have to fix this by replacing the Dispatchers with a TestCoroutineDispatcher. Like this:

我们必须通过用TestCoroutineDispatcher.替换Dispatchers来解决此TestCoroutineDispatcher. 像这样:

@ExperimentalCoroutinesApi
class ArticleViewModelTest {
private val testDispatcher = TestCoroutineDispatcher()

@Before
fun setup() {
Dispatchers.setMain(testDispatcher)
}

@After
fun tearDown() {
Dispatchers.resetMain()
testDispatcher.cleanupTestCoroutines()
}

@Test
fun addLikeCount() = runBlocking {
val
articleViewModel = ArticleViewModel()
articleViewModel.addLikeCount()
Assert.assertEquals(1, articleViewModel.getLikeCount())
}}

测试匕首(替换模块) (Testing Dagger (Replace module))

If you provide dependencies via dagger and then in test class, you want to provide with mock value. Do the following:

如果通过匕首提供依赖关系,然后在测试类中,则要提供模拟值。 请执行下列操作:

  1. dagger-compiler need to be included in test:

    测试中需要包含dagger-compiler

    dagger-compiler need to be included in test:kaptTest "com.google.dagger:dagger-compiler:$dagger2_version"

    测试中需要包含dagger-compilerkaptTest“ com.google.dagger:dagger-compiler:$ dagger2_version”

  2. Create test module in test’s source set:

    在测试的源集中创建测试模块:
@Module
object TestConstantModule {
@Provides
@Named(ENDPOINT)
fun provideEndpoint(): String {
return "http://127.0.0.1:8080"
}
}

3. Create test component in test’s source set, using the test module. We can implement the app component to use it’s methods:

3.使用测试模块在测试的源集中创建测试组件。 我们可以实现应用程序组件以使用其方法:

@Singleton
@Component(modules = [TestConstantModule::class])
interface TestAppComponent: AppComponent {
@Component.Builder
interface Builder {
fun build(): TestAppComponent
}
}

4. Then we can use test component in test classes:

4.然后,我们可以在测试类中使用测试组件:

DaggerTestAppComponent.builder().build()

使用MockWebServer进行测试 (Testing with mockwebserver)

We use mockwebserver to run server on local machine instead of calling the real API. Here step to do it:

我们使用模拟网络服务器在本地计算机上运行服务器,而不是调用真正的API。 请按以下步骤操作:

  1. Add mockwebserver to project

    将MockWebServer添加到项目
testImplementation("com.squareup.okhttp3:mockwebserver:$mockwebserverVersion")

2. Replace the real endpoint with local endpoint: “http://127.0.0.1:8080”

2.用本地端点替换真实端点:“ http://127.0.0.1:8080”

3. In test class, we start webserver in @Before and shutdown in @After:

3.在测试类中,我们在@Before中启动Web服务器,并在@After中关闭:

@Before
fun setup() {
mockWebServer.start(8080)
}

@After
fun teardown() {
mockWebServer.shutdown()
}

4. In test function, we write scenario for this mockWebServer:

4.在测试功能中,我们为此模拟WebServer编写方案:

@ExperimentalCoroutinesApi
@Test
fun successResponseMockWebServer() {
mockWebServer.dispatcher = object : Dispatcher() {
override fun dispatch(request: RecordedRequest): MockResponse {
return MockResponse().setResponseCode(200)
.setBody(MockResponseFileReader.response("success.json"))
}
}

.....
}

Now our code will call to local server then return response as we set.

现在,我们的代码将调用本地服务器,然后返回设置的响应。

File “success.json” should be placed in src/test/resources. The code for MockResponseFileReader is below:

文件“ success.json”应放在s rc / test / resources中。 的代码 MockResponseFileReader在下面:

object MockResponseFileReader {

fun response(path: String): String {
val reader = InputStreamReader(this.javaClass.classLoader!!.getResourceAsStream(path))
val content = reader.readText()
reader.close()

return content
}

}

需要应用程序/活动的单元测试 (Unit Test that need Application/Activity)

AndroidX Test is a collection of libraries for testing. It includes classes and methods that give you versions of components like Applications and Activities. We need these dependencies in gradle:

AndroidX Test是用于测试的库的集合。 它包括类和方法,这些类和方法为您提供组件的版本,例如“应用程序”和“活动”。 我们在gradle中需要这些依赖项:

// AndroidX Test - JVM testing
testImplementation "androidx.test.ext:junit-ktx:$androidXTestExtKotlinRunnerVersion"

testImplementation "androidx.test:core-ktx:$androidXTestCoreVersion"

testImplementation "org.robolectric:robolectric:$robolectricVersion"

Add JUnit Test Runner, add @RunWith(AndroidJUnit4::class)above your test class. Then when need components, call the appropriate method, such as:

添加JUnit测试运行器,在测试类上方添加@RunWith(AndroidJUnit4::class) 。 然后在需要组件时,调用适当的方法,例如:

ApplicationProvider.getApplicationContext()

One of the benefits of the AndroidX Test APIs is that they are built to work both for local tests and instrumented tests. Roboelectric will help to provide simulated Android environment.

AndroidX测试API的好处之一是它们被构建为既可以用于本地测试,也可以用于仪器化测试。 Roboelectric将帮助提供 模拟的Android环境。

与房间一起测试 (Test with Room)

While testing with room, we must run under a registering instrumentation. So add@RunWith(AndroidJUnit4::class) above your test class.

在进行带空间测试时,我们必须在注册工具下运行。 因此,在测试类上方添加@RunWith(AndroidJUnit4::class)

Create an in-memory database using Room.inMemoryDatabaseBuilder. Normal databases are meant to persist. By comparison, an in-memory database will be completely deleted once the process that created it is killed, since it's never actually stored on disk. Always use and in-memory database for your tests.

使用Room.inMemoryDatabaseBuilder创建内存数据库 普通数据库将保留下来。 相比之下,内存数据库一旦被创建的进程被杀死就将被完全删除,因为它实际上从未存储在磁盘上。 始终使用内存数据库进行测试。

@Before
fun initDb() {
// Using an in-memory database so that the information stored here disappears when the
// process is killed.
database = Room.inMemoryDatabaseBuilder(
getApplicationContext(),
ToDoDatabase::class.java
)
}@After
fun cleanUp() {
database.close()
}

We use .allowMainThreadQueries() to allow DAO run on main thread. Without that, we have to run it on different thread, that’s make our test case more complex. As we test the login only, not the UI, so just allow it to run on Main Thread.

我们使用.allowMainThreadQueries()允许DAO在主线程上运行。 否则,我们必须在不同的线程上运行它,这会使我们的测试用例更加复杂。 由于我们仅测试登录名而不是UI,因此只允许其在Main Thread上运行。

In test case, just get the DAO from our test database and write test case for it. Normally, we try to insert some mock entities to database and then getEntity to verify data available in database.

在测试用例中,只需从我们的测试数据库中获取DAO并为其编写测试用例。 通常情况下,我们尝试将一些模拟实体数据库,然后getEntity验证数据库中的数据。

// runBlocking is used here because of https://github.com/Kotlin/kotlinx.coroutines/issues/1204
// TODO: Replace with runBlockingTest once issue is resolved
@Test
fun saveTask_retrievesTask() = runBlocking {
// GIVEN - A new task saved in the database.
val newTask = Task("title", "description", false)
localDataSource.saveTask(newTask)

// WHEN - Task retrieved by ID.
val result = localDataSource.getTask(newTask.id)

// THEN - Same task is returned.
assertThat(result.succeeded, `is`(true))
result as Success
assertThat(result.data.title, `is`("title"))
assertThat(result.data.description, `is`("description"))
assertThat(result.data.isCompleted, `is`(false))
}

首选项 (Preferences)

https://developer.android.com/training/testing/fundamentals

https://developer.android.com/training/testing/fundamentals

https://phauer.com/2019/modern-best-practices-testing-java/

https://phauer.com/2019/modern-best-practices-testing-java/

https://testing.googleblog.com/

https://testing.googleblog.com/

https://github.com/square/okhttp/tree/master/mockwebserver

https://github.com/square/okhttp/tree/master/mockwebserver

翻译自: https://medium.com/@ngodinhduyquang/android-testing-case-by-case-bcd79593849c

android 逐帧动画

 类似资料: