[Lofi Time Radio App] TDD + Network Module (Phần 1)
Chào bạn! Trong bài đăng này, tôi sẽ nói về TDD và phần đầu tiên trong ứng dụng của tôi “Mô-đun mạng”.
TDD là gì?
Phát triển theo hướng thử nghiệm là một phương pháp phát triển phần mềm về cơ bản nói rằng đối với mỗi đơn vị phần mềm, nhà phát triển phần mềm phải:
Xác định một bộ kiểm tra cho đơn vị đầu tiên
Làm cho mã hoạt động, kiểm tra thất bại
Sau đó thực hiện các đơn vị
Cuối cùng xác minh rằng việc thực hiện đơn vị làm cho các bài kiểm tra thành công.
Đã đến lúc bắt đầu viết mã!!!
Xác định một bộ kiểm tra cho đơn vị đầu tiên
final class RemoteStationLoaderTests: XCTestCase {
func test_requestURLs() {
let client = HTTPClient()
let loader = RemoteStationLoader(client: client)
let url = URL(string: "https://any-url.com")!
loader.load(url: url)
XCTAssertEqual(client.requestURLs, [url])
}
}
class RemoteStationLoader {
private let client: HTTPClient
init(client: HTTPClient) {
self.client = client
}
func load(url: URL) {
}
}
final class HTTPClient {
var requestURLs = [URL]()
func load(url: URL) {
}
}
final class RemoteStationLoaderTests: XCTestCase {
func test_requestURLs() {
let client = HTTPClient()
let loader = RemoteStationLoader(client: client)
let url = URL(string: "https://any-url.com")!
loader.load(url: url)
XCTAssertEqual(client.requestURLs, [url])
}
}
Cuối cùng xác minh rằng việc thực hiện đơn vị làm cho các bài kiểm tra thành công.
class RemoteStationLoader {
private let client: HTTPClient
init(client: HTTPClient) {
self.client = client
}
func load(url: URL) {
client.load(url: url)
}
}
final class HTTPClient {
var requestURLs = [URL]()
func load(url: URL) {
requestURLs.append(url)
}
}
final class RemoteStationLoaderTests: XCTestCase {
func test_requestURLs() {
let client = HTTPClient()
let loader = RemoteStationLoader(client: client)
let url = URL(string: "https://any-url.com")!
loader.load(url: url)
XCTAssertEqual(client.requestURLs, [url])
}
}
Như bạn cấu trúc app ở trên, mình sẽ tạo framework hỗ trợ mọi nền tảng (iOS và macOS) có tên là LofiTimeRadioStation.
API trạm (Mô-đun mạng)
Như mình trước khi học CHƯƠNG TRÌNH iOS LEAD ESSENTIALS , trong project cá nhân mình thường tạo 1 class singleton tên là Service
public final class StationLoader {
public let shared = StationLoader()
private init() {}
func load(url: URL) {
// Code
}
}
Đảm bảo họ chỉ có một ví dụ
Tôi làm cho lớp có thể có nhiều hơn một phiên bản để tôi có thể tạo spyđể tránh gọi dịch vụ thực trong bài kiểm tra đơn vị
public final class StationLoader {
public var shared = StationLoader()
...
}
public protocol HTTPClient {
func load(url: URL)
}
public final class StationLoader {
private let client: HTTPClient
public init(client: HTTPClient) {}
}
// Unit-test
func test_load() {
let spy = HTTPClientSpy()
let loader = StationLoader(client: spy)
...
}
private class HTTPClientSpy: HTTPClient {
func load(url: URL) {}
}
Nhiều lợi ích hơn từ giao thức, tôi có thể dễ dàng thay đổi lớp triển khai từ giao thức và không thay đổi hành vi của mã sản xuất
public protocol HTTPClient {
func load(url: URL)
}
public class URLSessionHTTPClient: HTTPClient {
func load(url: URL) {}
}
public class AlamofireHTTPClient: HTTPClient {
func load(url: URL) {}
}
// Main Module
class AnyClass {
private let client: HTTPClient
init(client: HTTPClient) {}
func request(url: URL) {
client.load(url: url)
}
}
let aClass = AnyClass(client: URLSessionHTTPClient())
// or
let aClass = AnyClass(client: AlamofireHTTPClient())
aClass.request(url: URL(string: "https://any-url.com")!)
Cảm ơn bạn đã đọc. Hẹn gặp lại sau!!

![Dù sao thì một danh sách được liên kết là gì? [Phần 1]](https://post.nghiatu.com/assets/images/m/max/724/1*Xokk6XOjWyIGCBujkJsCzQ.jpeg)



































