Skip to content

Commit 2ca026e

Browse files
committed
Implement UnitTests
- `RequestParametersEncoderTests` - `RequestBuilderTests` - `RequestBodyEncoderTests`
1 parent e98a648 commit 2ca026e

10 files changed

Lines changed: 366 additions & 7 deletions

File tree

Sources/NetworkLayer/Classes/Core/Authentification/AuthenticatorInterceptor.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ import Atomic
77
import Foundation
88
import NetworkLayerInterfaces
99

10-
/// <#Description#>
10+
/// A custom AuthenticatorInterceptor implementation that works with a specific type
11+
/// of Authenticator conforming to the IAuthenticator protocol.
1112
public final class AuthenticatorInterceptor<Authenticator: IAuthenticator>: IAuthenticatorInterceptor {
1213
// MARK: Types
1314

Sources/NetworkLayer/Classes/Core/Builders/RequestBuilder/RequestBuilder.swift

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,11 @@ final class RequestBuilder: IRequestBuilder {
2424

2525
// MARK: IRequestBuilder
2626

27-
func build(_ request: NetworkLayerInterfaces.IRequest, _: ((inout URLRequest) throws -> Void)?) throws -> URLRequest? {
28-
guard let url = URL(string: request.fullPath) else {
27+
func build(
28+
_ request: NetworkLayerInterfaces.IRequest,
29+
_ configure: ((inout URLRequest) throws -> Void)?
30+
) throws -> URLRequest? {
31+
guard let fullPath = request.fullPath, let url = URL(string: fullPath) else {
2932
throw URLError(.badURL)
3033
}
3134

@@ -45,6 +48,8 @@ final class RequestBuilder: IRequestBuilder {
4548
try requestBodyEncoder.encode(body: httpBody, to: &urlRequest)
4649
}
4750

51+
try configure?(&urlRequest)
52+
4853
return urlRequest
4954
}
5055

Sources/NetworkLayer/Classes/Extensions/IRequest+.swift

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@ import Foundation
77
import NetworkLayerInterfaces
88

99
extension IRequest {
10-
var fullPath: String {
11-
[domainName, path].joined(separator: "/")
10+
var fullPath: String? {
11+
if !domainName.isEmpty {
12+
return [domainName, path].joined(separator: "/")
13+
}
14+
return nil
1215
}
1316
}

Tests/NetworkLayerTests/Fakes/URLRequest+Fake.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import Foundation
77

88
extension URLRequest {
9-
static func fake() -> URLRequest {
10-
URLRequest(url: URL(string: "https://google.com/")!)
9+
static func fake(string: String = "https://google.com/") -> URLRequest {
10+
URLRequest(url: URL(string: string)!)
1111
}
1212
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
//
2+
// network-layer
3+
// Copyright © 2023 Space Code. All rights reserved.
4+
//
5+
6+
import Foundation
7+
@testable import NetworkLayer
8+
import NetworkLayerInterfaces
9+
10+
final class RequestBodyEncoderMock: IRequestBodyEncoder {
11+
var invokedEncode = false
12+
var invokedEncodeCount = 0
13+
var invokedEncodeParameters: (body: RequestBody, request: URLRequest)?
14+
var invokedEncodeParametersList = [(body: RequestBody, request: URLRequest)]()
15+
var stubbedEncodeError: Error?
16+
17+
func encode(body: RequestBody, to request: inout URLRequest) throws {
18+
invokedEncode = true
19+
invokedEncodeCount += 1
20+
invokedEncodeParameters = (body, request)
21+
invokedEncodeParametersList.append((body, request))
22+
if let error = stubbedEncodeError {
23+
throw error
24+
}
25+
}
26+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
//
2+
// network-layer
3+
// Copyright © 2023 Space Code. All rights reserved.
4+
//
5+
6+
import Foundation
7+
@testable import NetworkLayer
8+
9+
final class RequestParametersEncoderMock: IRequestParametersEncoder {
10+
var invokedEncode = false
11+
var invokedEncodeCount = 0
12+
var invokedEncodeParameters: (parameters: [String: String], request: URLRequest)?
13+
var invokedEncodeParametersList = [(parameters: [String: String], request: URLRequest)]()
14+
var stubbedEncodeError: Error?
15+
16+
func encode(parameters: [String: String], to request: inout URLRequest) throws {
17+
invokedEncode = true
18+
invokedEncodeCount += 1
19+
invokedEncodeParameters = (parameters, request)
20+
invokedEncodeParametersList.append((parameters, request))
21+
if let error = stubbedEncodeError {
22+
throw error
23+
}
24+
}
25+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
//
2+
// network-layer
3+
// Copyright © 2023 Space Code. All rights reserved.
4+
//
5+
6+
import Foundation
7+
import NetworkLayerInterfaces
8+
9+
final class RequestStub: IRequest {
10+
var stubbedDomainName: String! = ""
11+
12+
var domainName: String {
13+
stubbedDomainName
14+
}
15+
16+
var stubbedPath: String! = ""
17+
18+
var path: String {
19+
stubbedPath
20+
}
21+
22+
var stubbedHeaders: [String: String]!
23+
24+
var headers: [String: String]? {
25+
stubbedHeaders
26+
}
27+
28+
var stubbedParameters: [String: String]!
29+
30+
var parameters: [String: String]? {
31+
stubbedParameters
32+
}
33+
34+
var stubbedRequiresAuthentication: Bool! = false
35+
36+
var requiresAuthentication: Bool {
37+
stubbedRequiresAuthentication
38+
}
39+
40+
var stubbedTimeoutInterval: TimeInterval = 60
41+
42+
var timeoutInterval: TimeInterval {
43+
stubbedTimeoutInterval
44+
}
45+
46+
var stubbedHttpMethod: HTTPMethod = .get
47+
48+
var httpMethod: HTTPMethod {
49+
stubbedHttpMethod
50+
}
51+
52+
var stubbedHttpBody: RequestBody? = nil
53+
54+
var httpBody: RequestBody? {
55+
stubbedHttpBody
56+
}
57+
58+
var stubbedCachePolicy: URLRequest.CachePolicy = .useProtocolCachePolicy
59+
60+
var cachePolicy: URLRequest.CachePolicy {
61+
stubbedCachePolicy
62+
}
63+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
//
2+
// network-layer
3+
// Copyright © 2023 Space Code. All rights reserved.
4+
//
5+
6+
@testable import NetworkLayer
7+
import XCTest
8+
9+
// MARK: - RequestBodyEncoderTests
10+
11+
final class RequestBodyEncoderTests: XCTestCase {
12+
// MARK: Properties
13+
14+
private var sut: RequestBodyEncoder!
15+
16+
// MARK: XCTestCase
17+
18+
override func setUp() {
19+
super.setUp()
20+
sut = RequestBodyEncoder(jsonEncoder: JSONEncoder())
21+
}
22+
23+
override func tearDown() {
24+
sut = nil
25+
super.tearDown()
26+
}
27+
28+
// MARK: Tests
29+
30+
func test_thatRequestBodyEncoderEncodesBodyIntoRequest_whenTypeIsData() throws {
31+
// given
32+
var requestFake = URLRequest.fake()
33+
let data = Data()
34+
35+
// when
36+
try sut.encode(body: .data(data), to: &requestFake)
37+
38+
// then
39+
XCTAssertEqual(requestFake.httpBody, data)
40+
}
41+
42+
func test_thatRequestBodyEncoderEncodesBodyIntoRequest_whenTypeIsDictonary() throws {
43+
// given
44+
var requestFake = URLRequest.fake()
45+
let dictonary = ["test": "test"]
46+
47+
// when
48+
try sut.encode(body: .dictonary(dictonary), to: &requestFake)
49+
50+
// then
51+
let data = try JSONSerialization.data(withJSONObject: dictonary)
52+
XCTAssertEqual(requestFake.httpBody, data)
53+
}
54+
55+
func test_thatRequestBodyEncoderEncodesBodyIntoRequest_whenTypeIsEncodable() throws {
56+
// given
57+
var requestFake = URLRequest.fake()
58+
let object = DummyObject()
59+
60+
// when
61+
try sut.encode(body: .encodable(object), to: &requestFake)
62+
63+
// then
64+
let data = try JSONEncoder().encode(object)
65+
XCTAssertEqual(requestFake.httpBody, data)
66+
}
67+
}
68+
69+
// MARK: RequestBodyEncoderTests.DummyObject
70+
71+
private extension RequestBodyEncoderTests {
72+
struct DummyObject: Encodable {}
73+
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
//
2+
// network-layer
3+
// Copyright © 2023 Space Code. All rights reserved.
4+
//
5+
6+
@testable import NetworkLayer
7+
import XCTest
8+
9+
// MARK: - RequestBuilderTests
10+
11+
final class RequestBuilderTests: XCTestCase {
12+
// MARK: Properties
13+
14+
private var parametersEncoderMock: RequestParametersEncoderMock!
15+
private var requestBodyEncoderMock: RequestBodyEncoderMock!
16+
17+
private var sut: RequestBuilder!
18+
19+
// MARK: XCTestCase
20+
21+
override func setUp() {
22+
super.setUp()
23+
parametersEncoderMock = RequestParametersEncoderMock()
24+
requestBodyEncoderMock = RequestBodyEncoderMock()
25+
sut = RequestBuilder(
26+
parametersEncoder: parametersEncoderMock,
27+
requestBodyEncoder: requestBodyEncoderMock
28+
)
29+
}
30+
31+
override func tearDown() {
32+
parametersEncoderMock = nil
33+
requestBodyEncoderMock = nil
34+
sut = nil
35+
super.tearDown()
36+
}
37+
38+
// MARK: Tests
39+
40+
func test_thatRequestBuilderThrowsAnError_whenRequestIsNotValid() {
41+
// given
42+
let request = RequestStub()
43+
44+
// when
45+
var receivedError: NSError?
46+
do {
47+
_ = try sut.build(request, nil)
48+
} catch {
49+
receivedError = error as NSError
50+
}
51+
52+
// then
53+
XCTAssertEqual(receivedError, URLError(.badURL) as NSError)
54+
}
55+
56+
func test_thatRequestBuilderBuildsARequest() throws {
57+
// given
58+
let requestStub = RequestStub()
59+
requestStub.stubbedDomainName = .domainName
60+
requestStub.stubbedHeaders = .contentType
61+
requestStub.stubbedHttpMethod = .post
62+
requestStub.stubbedHttpBody = .dictonary(.item)
63+
requestStub.stubbedParameters = .contentType
64+
65+
// when
66+
var invokedConfigure = false
67+
let request = try sut.build(requestStub) { _ in invokedConfigure = true }
68+
69+
// then
70+
XCTAssertTrue(invokedConfigure)
71+
XCTAssertEqual(request?.allHTTPHeaderFields, .contentType)
72+
XCTAssertEqual(request?.httpMethod, "POST")
73+
XCTAssertEqual(parametersEncoderMock.invokedEncodeParameters?.parameters, .contentType)
74+
75+
if case let .dictonary(dict) = requestBodyEncoderMock.invokedEncodeParameters?.body {
76+
XCTAssertTrue(NSDictionary(dictionary: dict).isEqual(to: Dictionary.item))
77+
} else {
78+
XCTFail("body should be equal to a dictionary")
79+
}
80+
}
81+
}
82+
83+
// MARK: - Constants
84+
85+
private extension String {
86+
static let domainName = "https://google.com"
87+
}
88+
89+
private extension Dictionary where Self.Key == String, Self.Value == String {
90+
static let contentType = ["Content-Type": "application/json"]
91+
}
92+
93+
private extension Dictionary where Self.Key == String, Self.Value == Any {
94+
static let item = ["Content-Type": "application/json"]
95+
}

0 commit comments

Comments
 (0)