From 7e653b940c1ae77b07a0e0125a4ee0c4990765f5 Mon Sep 17 00:00:00 2001
From: Bliss Pisit Wetcha <pisit@nimblehq.co>
Date: Wed, 4 Oct 2023 18:04:41 +0700
Subject: [PATCH] [#508] Add network test

---
 Tuist/Interfaces/SwiftUI/Project/Podfile      |  1 +
 Tuist/Interfaces/UIKit/Project/Podfile        |  1 +
 {PROJECT_NAME}Tests/Sources/Dummy/.gitkeep    |  0
 .../Sources/Dummy/DummyNetworkModel.swift     | 14 ++++
 .../Dummy/DummyRequestConfiguration.swift     | 29 +++++++++
 .../Data/NetworkAPI/NetworkAPISpec.swift      | 64 +++++++++++++++++++
 .../Sources/Utilities/.gitkeep                |  0
 .../Sources/Utilities/NetworkStubber.swift    | 32 ++++++++++
 8 files changed, 141 insertions(+)
 delete mode 100644 {PROJECT_NAME}Tests/Sources/Dummy/.gitkeep
 create mode 100644 {PROJECT_NAME}Tests/Sources/Dummy/DummyNetworkModel.swift
 create mode 100644 {PROJECT_NAME}Tests/Sources/Dummy/DummyRequestConfiguration.swift
 create mode 100644 {PROJECT_NAME}Tests/Sources/Specs/Data/NetworkAPI/NetworkAPISpec.swift
 delete mode 100644 {PROJECT_NAME}Tests/Sources/Utilities/.gitkeep
 create mode 100644 {PROJECT_NAME}Tests/Sources/Utilities/NetworkStubber.swift

diff --git a/Tuist/Interfaces/SwiftUI/Project/Podfile b/Tuist/Interfaces/SwiftUI/Project/Podfile
index 6dbc2e01..7a0d2eed 100644
--- a/Tuist/Interfaces/SwiftUI/Project/Podfile
+++ b/Tuist/Interfaces/SwiftUI/Project/Podfile
@@ -7,6 +7,7 @@ def testing_pods
   pod 'Nimble', '~> 11.0'
   pod 'Sourcery'
   pod 'SwiftFormat/CLI'
+  pod 'OHHTTPStubs/Swift', :configurations => ['Debug Staging', 'Debug Production']
 end
 
 target '{PROJECT_NAME}' do
diff --git a/Tuist/Interfaces/UIKit/Project/Podfile b/Tuist/Interfaces/UIKit/Project/Podfile
index cf4fbd43..d5bdbf53 100644
--- a/Tuist/Interfaces/UIKit/Project/Podfile
+++ b/Tuist/Interfaces/UIKit/Project/Podfile
@@ -7,6 +7,7 @@ def testing_pods
   pod 'Nimble'
   pod 'Sourcery'
   pod 'SwiftFormat/CLI'
+  pod 'OHHTTPStubs/Swift', :configurations => ['Debug Staging', 'Debug Production']
 end
 
 target '{PROJECT_NAME}' do
diff --git a/{PROJECT_NAME}Tests/Sources/Dummy/.gitkeep b/{PROJECT_NAME}Tests/Sources/Dummy/.gitkeep
deleted file mode 100644
index e69de29b..00000000
diff --git a/{PROJECT_NAME}Tests/Sources/Dummy/DummyNetworkModel.swift b/{PROJECT_NAME}Tests/Sources/Dummy/DummyNetworkModel.swift
new file mode 100644
index 00000000..e6dfb70c
--- /dev/null
+++ b/{PROJECT_NAME}Tests/Sources/Dummy/DummyNetworkModel.swift
@@ -0,0 +1,14 @@
+//
+//  DummyNetworkModel.swift
+//
+
+import Foundation
+
+struct DummyNetworkModel: Decodable {
+
+    static let json =
+        """
+        {"message": "Hello"}
+        """
+    let message: String
+}
diff --git a/{PROJECT_NAME}Tests/Sources/Dummy/DummyRequestConfiguration.swift b/{PROJECT_NAME}Tests/Sources/Dummy/DummyRequestConfiguration.swift
new file mode 100644
index 00000000..1359b72b
--- /dev/null
+++ b/{PROJECT_NAME}Tests/Sources/Dummy/DummyRequestConfiguration.swift
@@ -0,0 +1,29 @@
+//
+//  DummyRequestConfiguration.swift
+//
+
+import Alamofire
+
+@testable import {PROJECT_NAME}
+
+struct DummyRequestConfiguration: RequestConfiguration {
+
+    var baseURL: String { "https://example.com" }
+
+    var endpoint: String { "" }
+
+    var method: Alamofire.HTTPMethod { .get }
+
+    var encoding: Alamofire.ParameterEncoding { URLEncoding.queryString }
+}
+
+extension DummyRequestConfiguration: RequestConfigurationStubable {
+
+    var sampleData: Data {
+        DummyNetworkModel.json.data(using: .utf8) ?? Data()
+    }
+
+    var path: String {
+        (try? url.asURL().path).string
+    }
+}
diff --git a/{PROJECT_NAME}Tests/Sources/Specs/Data/NetworkAPI/NetworkAPISpec.swift b/{PROJECT_NAME}Tests/Sources/Specs/Data/NetworkAPI/NetworkAPISpec.swift
new file mode 100644
index 00000000..c26bbdd0
--- /dev/null
+++ b/{PROJECT_NAME}Tests/Sources/Specs/Data/NetworkAPI/NetworkAPISpec.swift
@@ -0,0 +1,64 @@
+//
+//  NetworkAPISpec.swift
+//
+
+import Nimble
+import Quick
+
+@testable import {PROJECT_NAME}
+
+final class NetworkAPISpec: QuickSpec {
+
+    override func spec() {
+
+        describe("a NetworkAPI") {
+
+            var networkAPI: NetworkAPI!
+            var requestConfiguration: DummyRequestConfiguration!
+
+            describe("its performRequest") {
+
+                beforeEach {
+                    requestConfiguration = DummyRequestConfiguration()
+                }
+
+                afterEach {
+                    NetworkStubber.removeAllStubs()
+                }
+
+                context("when network returns value") {
+
+                    beforeEach {
+                        NetworkStubber.stub(requestConfiguration)
+                        networkAPI = NetworkAPI()
+                    }
+
+                    it("returns message as Hello") {
+                        let response = try await networkAPI.performRequest(
+                            requestConfiguration,
+                            for: DummyNetworkModel.self
+                        )
+                        expect(response.message) == "Hello"
+                    }
+                }
+
+                context("when network returns error") {
+
+                    beforeEach {
+                        NetworkStubber.stub(requestConfiguration, data: Data(), statusCode: 400)
+                        networkAPI = NetworkAPI()
+                    }
+
+                    it("throws error") {
+                        await expect {
+                            try await networkAPI.performRequest(
+                                requestConfiguration,
+                                for: DummyNetworkModel.self
+                            )
+                        }.to(throwError())
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/{PROJECT_NAME}Tests/Sources/Utilities/.gitkeep b/{PROJECT_NAME}Tests/Sources/Utilities/.gitkeep
deleted file mode 100644
index e69de29b..00000000
diff --git a/{PROJECT_NAME}Tests/Sources/Utilities/NetworkStubber.swift b/{PROJECT_NAME}Tests/Sources/Utilities/NetworkStubber.swift
new file mode 100644
index 00000000..85ccb721
--- /dev/null
+++ b/{PROJECT_NAME}Tests/Sources/Utilities/NetworkStubber.swift
@@ -0,0 +1,32 @@
+//
+//  NetworkStubber.swift
+//
+
+import OHHTTPStubs
+
+protocol RequestConfigurationStubable {
+
+    var sampleData: Data { get }
+    var path: String { get }
+}
+
+enum NetworkStubber {
+
+    static func removeAllStubs() {
+        HTTPStubs.removeAllStubs()
+    }
+
+    static func stub(
+        _ request: RequestConfigurationStubable,
+        data: Data? = nil,
+        statusCode: Int32 = 200
+    ) {
+        OHHTTPStubs.stub(condition: isPath(request.path)) { _ in
+            HTTPStubsResponse(
+                data: data ?? request.sampleData,
+                statusCode: statusCode,
+                headers: nil
+            )
+        }
+    }
+}