opensips 模拟呼叫
两全其美的 (The Best of Both Worlds)
Unit testing is a great way to ensure your app works across many iterations. Sometimes it’s important to ensure things work asynchronously, but also to ensure that you have consistent data. When you don’t control your remote source, this can be virtually impossible.
单元测试是确保您的应用程序可以进行多次迭代的好方法。 有时,确保事物异步运行很重要,但同时也要确保您拥有一致的数据,这一点很重要。 当您不控制远程源时,这几乎是不可能的。
动态环境中的单元测试问题 (The problem with unit testing in a dynamic environment)
If you're unit testing a network call, chances are that you aren’t just testing to make sure that you can connect to the network, but to ensure that you can do something meaningful with it, and perhaps to check that something happens afterwards. That something afterwards usually relies on consistent data.
如果您正在对网络呼叫进行单元测试,则可能不仅是在进行测试以确保可以连接到网络,还在于确保可以对网络做一些有意义的事情,并且可能要检查之后是否发生了某些事情。 。 事后通常依赖于一致的数据。
For example, I want to connect to server A, receive some data, decode the data, then test to make sure the data contains a specific value. That’s an easy enough unit test to write, but there’s an inherent problem in the nature of networking. Things change — and that’s normally a good thing. But when you’re unit testing, you need to rely on things staying the same (such as that specific value from earlier).
例如,我要连接到服务器A,接收一些数据,对数据进行解码,然后进行测试以确保数据包含特定值。 这是编写起来很容易的单元测试,但是网络的本质存在一个固有的问题。 事情发生了变化,这通常是一件好事。 但是,当您进行单元测试时,您需要依靠保持不变的东西 (例如之前的特定值)。
You also need to test your app in as close to an environment as it will be running in when it’s live (i.e. testing in an asynchronous manner). This is impossible with conventional network tests over the long term (unless the data is guaranteed to be static)…
您还需要在应用程序运行时在尽可能接近其运行时的环境中对其进行测试(即以异步方式进行测试)。 长期而言,使用常规网络测试是不可能的(除非保证数据是静态的)…
输入模拟数据加载器 (Enter the Mock Data Loader)
This mock data loader relies on a very simple protocol that very closely mimics URLSession.shared.dataTask. In fact, you can also use this protocol to extend URLSession so that you never forget .resume() again.
该模拟数据加载器依赖于非常简单的协议,该协议非常类似于URLSession.shared.dataTask。 实际上,您还可以使用此协议扩展URLSession,以使您再也不会忘记.resume() 。
![The NetworkLoader protocol](https://miro.medium.com/max/9999/1*2LTMVwqIjgyDj_b2lPvJLQ.png)
protocol NetworkLoader { func loadData(using request: URLRequest, with completion: @escaping (Data?, URLResponse?, Error?) -> Void)}
Now that we have our super simple, yet very powerful protocol defined, let’s make a simple class that conforms to it and uses it. This is the class we’ll use for our unit test later. It has the same properties the loadData method takes. It also includes a request property. In this example, the request is private(set) so it can’t be read outside of the class, but it can be set. If you need to change this, you can and it should still function the same.
现在,我们已经定义了超级简单但功能非常强大的协议,让我们创建一个符合它并使用它的简单类。 这是我们稍后将用于单元测试的类。 它具有与loadData方法相同的属性。 它还包括一个request属性。 在此示例中,请求是private(set),因此无法在类外部读取,但可以将其设置。 如果您需要更改此设置,则可以并且它仍应具有相同的功能。
![Mock Data Loader](https://miro.medium.com/max/9999/1*sI-KP6nVYMpSAdGNk79Wvw.png)
class MockDataLoader: NetworkLoader { let data: Data?
let response: URLResponse?
let error: Error? init(data: Data?, response: URLResponse?, error: Error?) {
self.data = data
self.response = response
self.error = error
} private(set) var request: URLRequest? func loadData(using request: URLRequest, with completion: @escaping (Data?, URLResponse?, Error?) -> Void) {//force this to be async by placing the completion on a background thread DispatchQueue.main.asyncAfter(deadline: .now() + 0.005) {
self.request = request //used to test after this completes
completion(self.data, self.response, self.error)
}
}
}
![DispatchQueue.main.asyncAfter makes our call async](https://miro.medium.com/max/9999/1*RW-Z5wRxRXPJM-9SgWK8TQ.png)
And let’s make some quick Mock JSON data we can use to decode with the JSONDecoder
让我们快速制作一些可用于通过JSONDecoder解码的Mock JSON数据
![Image for post](https://miro.medium.com/max/9999/1*S6qyV4bww3XovrlHPoh7fg.png)
let data = """
{
"money":"1,000"
}
""".data(using: .utf8)!
Now lets write a test that loads this data, decodes the value of “money” (“1,000”) and checks to see if we can convert that to an Int
现在,让我们编写一个测试来加载此数据,解码“ money”(“ 1,000”)的值,并检查是否可以将其转换为Int
Remember, to write an asynchronous unit test, we’ll need to use an expectation or our test won’t work… even though the test will report that it succeeded, it didn’t actually do the asynchronous part.
记住,要编写一个异步单元测试,我们需要使用期望值,否则我们的测试将无法正常工作……即使测试报告成功,但实际上并没有完成异步部分。
![Unit test testing the mock data we just created](https://miro.medium.com/max/9999/1*UTDBGLOAwHKK257Ewh5Qng.png)
func testDecodingMockData() { let expectation = self.expectation(description: "\(#file), \(#function): WaitForDecodingMockData") //using the data constant we just created
let mockDataLoader = MockDataLoader(
data: data,
response: nil,
error: nil
) let request = URLRequest(url: URL(string: "www.google.com")!) mockDataLoader.loadData(using: request) { (data, nil, error) in
XCTAssertNotNil(mockDataLoader.data) XCTAssertNil(mockDataLoader.response)
XCTAssertNil(mockDataLoader.error)
let decoder = JSONDecoder() let dictionary = try? decoder.decode([String:String].self, from: mockDataLoader.data!)
let money = dictionary?["money"]
XCTAssertNotNil(Int(money!))
expectation.fulfill()
}wait(for: [expectation], timeout: 1.0)}
And just like that, we’ve written a consistent test that mocks the state of our app and proves that we can convert a String to an Int. The test runs asynchronously, so we can test that other functions would fire after this one completes among other things.
就像这样,我们编写了一个一致的测试来模拟应用程序的状态,并证明我们可以将String转换为Int。 该测试异步运行,因此我们可以测试该功能完成后是否会触发其他功能。
As I mentioned earlier, it can also be used to extend URLSession. As such, I can make a class with this property and use it to switch between live and mock data on the fly.
如前所述,它也可以用于扩展URLSession。 这样,我可以使用此属性创建一个类,并使用它在实时数据和模拟数据之间进行切换。
![Image for post](https://miro.medium.com/max/9999/1*L_s3MTgXoSFYzf4vXbSGLQ.png)
class NetworkService {
///used to switch between live and Mock Data
var dataLoader: NetworkLoader init(dataLoader: NetworkLoader = URLSession.shared) {
self.dataLoader = dataLoader
}
}
翻译自: https://medium.com/swlh/unit-testing-with-mock-network-calls-b7fe3af0bca5
opensips 模拟呼叫