A grouping of helpers used to improve the testing experience.
TDD is a great way to write code. It improves code quality and brings great joy.
But it also brings a lot of boilerplate.
These test helpers are designed with TDD in mind. They can help with writing tests that are clear and reduce boilerplate code.
This Equatable Identifiable Error can be used to help test errors thrown by dependencies.
Usage:
func test_failingFetchX_fetchY_fails() async throws {
let error = AnyError()
let (sut, _) = makeSUT(fetchXResult: .failure(error))
let capturedError = await captureError(from: try await sut.fetchY())
XCTAssertNotNil(error)
XCTAssertEqual(capturedError as? AnyError, error)
}
Accessing an array with an index that is out of bounds will cause a runtime crash in swift. If this happens in a test, the default Xcode settings will pause execution and open the debugger. Also the error message is not very clear.
This array subscript helps by successfully failing the test and showing a nice error message at the point of access.
Usage:
let (sut, spy) = makeSUT()
sut.doLogin()
XCTAssertEqual(spy.messages.count, 1)
XCTAssertEqual(spy.messages[index: 0], .userLogin)
The do
-try
-catch
dance with optional captured errors can add a lot of boilerplate to your tests.
This helps by turning the error capture into a single line. A non-optional error is returned or a test failure happens if an error is not thrown.
Usage:
func test_failingFetchX_fetchY_fails() async throws {
let error = AnyError()
let (sut, _) = makeSUT(fetchXResult: .failure(error))
let capturedError = await captureError(from: try await sut.fetchY())
XCTAssertEqual(capturedError as? AnyError, error)
}
Subscribing to a Publisher and managing the AnyCancellable in a test leads to a lot of typing, and can distract from the main focus of the test.
This function helps to make your code more clear. Put the actions which will cause the publishes
in the block and captureIsMainThread
will return the results.
Usage:
func test_refreshTitle_publishesOnMainThread() async throws {
let (sut, _) = makeSUT()
let capturedIsMainThread = await captureIsMainThread(for: sut.$title) {
await sut.refreshTitle()
}
XCTAssertEqual(capturedIsMainThread.count, 1)
XCTAssertEqual(capturedIsMainThread[index: 0], true)
}
Subscribing to a Publisher and managing the AnyCancellable in a test leads to a lot of typing, and can distract from the main focus of the test.
This function helps to make your code more clear. Put the actions which will cause the publishes
in the block and captureOutput
will return the results.
Usage:
func test_refreshTitle_setsIsRefreshing() async throws {
let (sut, _) = makeSUT()
let capturedOutput = await captureOutput(for: sut.$isLoading) {
await sut.refreshTitle()
}
XCTAssertEqual(capturedOutput.count, 2)
XCTAssertEqual(capturedOutput[index: 0], true)
XCTAssertEqual(capturedOutput[index: 1], false)
}
Memory leaks occur when an object is not properly released from memory. This often happens when mistakenly creating a circular strong reference.
This method will help to catch these kinds of errors by making sure that your object has been deallocated by the end of the test. It is most useful to use it in your makeSUT method.
Usage:
private func makeSUT(
userLoginResult: Result<Void, Error> = .success(()),
file: StaticString = #file,
line: UInt = #line
) -> (sut: LoginFlow, spy: Spy) {
let spy = Spy(userLoginResult: userLoginResult)
let sut = LoginFlow(service: spy)
expectWillDeallocate(for: sut, file: file, line: line)
expectWillDeallocate(for: spy, file: file, line: line)
return (sut, spy)
}
For sample code showing how to use a helper function, check out the test code for that helper.
Add this package as a dependency to the test target of your Xcode project.
TDDKit
product and add it to your project's test target Note: NOT your main target.Add this package as a dependency to the test target of your Swift package.
// swift-tools-version: 5.7
import PackageDescription
let package = Package(
name: "SampleProduct",
products: [
.library(name: "SampleProduct", targets: ["SampleProduct"])
],
dependencies: [
.package(name: "TDDKit", url: "https://github.com/andybezaire/TDDKit.git", from: "1.0.0")
],
targets: [
.target(name: "SampleProduct", dependencies: []),
.testTarget(name: "SampleProductTests", dependencies: ["SampleProduct", "TDDKit"])
]
)
Please do not hesitate to open a GitHub issue for any questions or feature requests.
"TDDKit" is available under the MIT license. See the LICENSE file for more info.
Copyright (c) 2023 Andy Bezaire
Created by: andybezaire
link |
Stars: 1 |
Last commit: 2 weeks ago |
Swiftpack is being maintained by Petr Pavlik | @ptrpavlik | @swiftpackco | API | Analytics