安卓& Kotlin Books Android测试驱动驱动的教程

10
测试网络层 由Victoria Gonda撰写

你住在互联网的时间里,你的应用可能也很可能。大多数应用程序以某种方式通过网络连接到API,给予它们 网络层。由于这通常是您应用的关键部分,因此它应该推理您应该测试它。这是您在本章中学习测试的内容!一路走来学习:

  • 用于测试网络层的工具。
  • 如何提供可靠的测试数据。
  • 关于维护网络层测试的重要事项。

在测试与网络上的数据的互动时需要考虑的一件事是,在运行自动测试时,您就不会 实际上 想要进行网络调用。网络调用是不可预测的。由于网络连接,服务器,Internet服务提供商等,调用可能会失败。您不能总是知道这些组件中的任何一个状态。你需要你的测试 可重复的可预见此外,这种依赖于摇杆网络不会在这里工作。

您可以使用工具来测试网络层而无需击中网络,这就是本章将重新关注的网络。您将查看三个要添加到您的测试工具箱的工具:

  • Mockwebserver 从网络请求中嘲笑响应
  • Mockito. 嘲笑你的API响应
  • 骗子 for data creation

入门

在本章中,您将在一个调用的应用程序上工作 妙处。这是一个应用程序,每次按下按钮都会向您展示一个新的随机笑话。要开始,请在本章中查找原料中的Starter项目,并在Android Studio中打开它。

运行应用程序,但你看不到很多:

您将在第11章“用户界面”结束之前,没有UI供您使用。在此之前,您将看到您的进度以网络层的绿色测试的形式提出!

Pondline应用程序对网络进行一次呼叫:获取随机笑话的那个。您将三次测试此请求,每个请求使用其他工具。在您开始测试之前,您应该在手头上携带几个文件:

  • Jokeservice.kt.:这是 改造 您将声明您的网络请求的服务。

  • repository.kt.: This file defines RepositoryImpl. It’s the glue that connects the network layer with the rest of the app.

  • 笑话 : Your Joke data model lives here. You can see that it has values for the ID and joke.

笔记 :它有用,但不是必需的,熟悉本章的改造。要了解更多信息,请转到“Android网络教程:入门” //www.ohdvia.icu/2-android-networking-tutorial-getting-started.

创建测试文件

你不能在没有放置的地方写下测试!首先,创建测试文件。创建 Jokeservicetest.kt.App‣src‣测试‣java‣com‣raywenderlich‣android‣打孔线 没有阶级声明。您将在此文件中放置所有三个测试类以便于比较。请注意,此测试在 测试 并不是 和 roidtest.。这是对的,您不需要Android框架在使用这些工具时测试网络层!他们会跑得很好,快速奔跑。如果您在自己的应用程序中的集成测试中有一个地方,您还可以在Android测试中使用MockWebServer。

调查API.

大多数改造的样板已经为您设置。你可以偷看 Koinmodules.kt. 如果您有兴趣查看设置。在测试之前,您关心的是您正在测试和实现的特定端点。

{
  "id":17,
  "joke":"Where do programmers like to hangout? The Foo Bar.",
  "created_at":"2018-12-31T21:08:53.772Z",
  "updated_at":"2018-12-31T21:36:33.937Z",
  "url":"//rw-punchline.herokuapp.com/jokes/17.json"
}

使用mockwebserver

你将学习的第一个工具是 Mockwebserver。这是一个图书馆 Okhttp. 这允许您在测试中运行本地HTTP服务器。有了它,您可以指定您希望服务器返回的内容并对所需请求执行验证。

 测试 Implementation "com.squareup.okhttp3:mockwebserver:3.12.0"
class JokeServiceTestUsingMockWebServer {
}

设置Mockwebserver

Mockwebserver有一个测试规则,您可以用于网络测试。它是一个可脚本的Web服务器。您将提供IT响应,它将根据要求返回它们。将规则添加到您的测试类:

@get:Rule
val mockWebServer = MockWebServer()
private val retrofit by lazy {
  Retrofit.Builder()
      // 1
      .baseUrl(mockWebServer.url("/"))
      // 2
      .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
      // 3
      .addConverterFactory(GsonConverterFactory.create())
      // 4
      .build()
}
private val jokeService by lazy {
  retrofit.create(JokeService::class.java)
}

运行mockwebserver

Your JokeService should have a function, getRandomJoke(), that returns a random joke. To handle the asynchronous nature of network calls, you will use RxJava. If you’re not familiar with RxJava, this is all you need to know: getRandomJoke() will return a Single of type Joke. Parties will subscribe to this Single 和 receive an event when it 发射 a Joke. RxJava brings a lot of power, but, for the sake of this exercise, you can think of it as a way to perform a 打回来 .

@Test
fun getRandomJokeEmitsJoke() {
}

脚本响应

既然你有Mockwebserver设置为接收请求,现在是时候脚本才能返回!

private val testJson = """{ "id": 1, "joke": "joke" }"""
// 1
mockWebServer.enqueue(
    // 2
    MockResponse()
        // 3
        .setBody(testJson)
        // 4
        .setResponseCode(200))

编写模仿网格测试

Phew! With all that set up, it’s finally time to finish writing your test. Add these two lines to the bottom of getRandomJokeEmitsJoke(). There will be an error at getRandomJoke() because you haven’t created it yet:

// 1
val testObserver = jokeService.getRandomJoke().test()
// 2
testObserver.assertValue(Joke("1", "joke"))
fun getRandomJoke(): Single<Joke>

@GET("//ohdvia.icu")
fun getRandomJoke(): Single<Joke>

@GET("joke.json")

重构您的测试

You may feel like there’s a code-smell in the way you’re hard coding the values for the ID and joke. Thankfully, you can change that! Because you’re creating the JSON String 在 the test, you can create it the way you like. Make a constant for the ID and the joke. By putting them outside the test class at the file level you’ll be able to use them in your other tests too:

private const val id = "6"
private const val joke =
    "How does a train eat? It goes chew, chew"
private val testJson = """{ "id": $id, "joke": "$joke" }"""
 测试 Observer.assertValue(Joke(id, joke))

维护测试数据

You just set up some test data and wrote some tests. Now, imagine the JSON response had many more properties and you had more endpoints to test. Then, imagine the format of the response changed. Maybe instead of joke as a String, it contained an object with different translations of the joke. You need to make sure you update your tests with this change when it happens. If you don’t test the new type of response, your tests are no longer accurate.

测试终点

You may be having some doubts about that last test. If it will pass with any endpoint with the same base URL, what is it testing? Is it testing that the response is correctly parsed into a Joke object? While it’s important to know your data is represented correctly, Mockwebserver does help you test the endpoint too! Next you’ll add a test that the endpoint is correct.

@Test
fun getRandomJokeGetsRandomJokeJson() {
  // 1
  mockWebServer.enqueue(
      MockResponse()
          .setBody(testJson)
          .setResponseCode(200))
  // 2
  val testObserver = jokeService.getRandomJoke().test()
  // 3
  testObserver.assertNoErrors()
  // 4
  assertEquals("/random_joke.json",
      mockWebServer.takeRequest().path)
}

@GET("random_joke.json")

嘲笑服务

Depending on your app and your team, it may be enough to know that the service methods are available and you’re using them correctly. This can be done using Mockito. , which you first learned in Chapter 7, “Mockito简介.” In this test you will also be concerned with getRandomJoke(), but your test will worry more about its interaction with the respository.

class JokeServiceTestMockingService {
}
private val jokeService: JokeService = mock()
private val repository = RepositoryImpl(jokeService)
@Test
fun getRandomJokeEmitsJoke() {
  // 1
  val joke = Joke(id, joke)
  // 2
  whenever(jokeService.getRandomJoke())
      .thenReturn(Single.just(joke))
  // 3
  val testObserver = repository.getJoke().test()
  // 4
  testObserver.assertValue(joke)
}

return service.getRandomJoke()

使用Faker进行测试数据

在本章中,您一直在使用相同的无聊旧测试数据:

private const val id = "6"
private const val joke =
    "How does a train eat? It goes chew, chew"
 测试 Implementation 'com.github.javafaker:javafaker:0.16'
class JokeServiceTestUsingFaker {
}
var faker = Faker()
private val jokeService: JokeService = mock()
private val repository = RepositoryImpl(jokeService)
@Test
fun getRandomJokeEmitsJoke() {
  val joke = Joke(
      faker.idNumber().valid(),
      faker.lorem().sentence())

  whenever(jokeService.getRandomJoke())
      .thenReturn(Single.just(joke))
  val testObserver = repository.getJoke().test()

  testObserver.assertValue(joke)
}

决定使用什么工具

您在本章中学习了这一章和之前的章节中的许多工具。你如何决定使用哪个?什么时候是一个单位测试,或者什么时候进行集成测试?希望你开始思考如何混合和匹配这些东西,例如你如何使用伪造者和模仿的最后一次测试。

关键点

  • 为了使您的测试可重复和可预测,您不应该在测试中进行真正的网络呼叫。
  • 您可以使用MockWebServer来脚本请求响应,并验证是否调用了正确的端点。
  • 如何创建和维护测试数据将根据您的应用需求而更改。
  • 如果您不需要Mockwebserver的细粒度控制,则可以使用Mockito模拟网络层。
  • 通过使用Faker库,您可以轻松创建随机的有趣的测试数据。
  • 决定哪些工具是正确的,需要时间来通过实验,试验和错误来学习。

然后去哪儿?

到目前为止你来了!从单元测试到集成,测试应用程序的许多部分。您仍然有一个非常重要的部分来学习如何测试:用户界面!你学习的所有事情都在达到这一点。在第11章“用户界面”中,您将学习如何自动执行这些点击屏幕并验证用户看到的内容。

有一个技术问题?想报告一个错误吗? 您可以向官方书籍论坛中的书籍作者提出问题和报告错误 这里 .

有反馈分享在线阅读体验吗? 如果您有关于UI,UX,突出显示或我们在线阅读器的其他功能的反馈,您可以将其发送到设计团队,其中表格如下所示:

© 2021 Razeware LLC

您可以免费读取,本章的部分显示为 混淆了 文本。解锁这本书,以及我们整个书籍和视频目录,带有Raywenderlich.com的专业订阅。

现在解锁

要突出或记笔记,您需要在订阅中拥有这本书或自行购买。