I'm studying unit testing in swift and I would like to know if I'm doing the right way.
Here I have the model
struct ChangePasswordRequest: Encodable {
var email: String
var newPassword: String
var token: String
}
And here the unit test
import XCTest
@testable import MyProject
class ChangePasswordRequestTests: XCTestCase {
var changePasswordRequest: ChangePasswordRequest!
override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
changePasswordRequest = nil
super.tearDown()
}
func testChangePasswordRequest() {
// given
let email = "[email protected]"
let newPassword = "abc123"
let token =
"""
rF1rHbYVCpcfyo6K2e7Q68QvKYdeOMt0vDXYehggGU3kx8XiuGmQylXabxbGAyUmSK9RnbL
"""
// when
changePasswordRequest = ChangePasswordRequest(email: email, newPassword: newPassword, token: token)
let requestJSON = """
{"email":"\(email)","token":"\(token)","newPassword":"\(newPassword)"}
"""
changePasswordRequest = ChangePasswordRequest(email: email, newPassword: newPassword, token: token)
guard let encodedRequest = try? JSONEncoder().encode(changePasswordRequest) else {
XCTFail()
return
}
// then
XCTAssertEqual(String(data: encodedRequest, encoding: .utf8)!, requestJSON, "Any JSON key differs from model.")
}
}
I never worked with unit test. It's possible to improve this test?
2 Answers 2
Some hints on Unit tests.
- Use dependency injection to make testing easier
- Create mocks to eliminate all external dependencies in tests
- Test units of code (functions, methods, classes)
Example of testable class:
class UserManager: UserManagerType {
let database: DatabaseType
init(database: DatabaseType) {
self.database = database
}
func getUser(withId id: String) -> User? {
return database.getItem(withPredicate: "id == \(id)")
}
func getAdmins() -> [User] {
return database.getItems(withPredicate: "type == admin")
}
}
protocol DatabaseType {
func getItems(withPredicate predicate: String) -> [User]
func getItem(withPredicate predicate: String) -> User?
}
Tests:
// create mock for database.
// Try to avoid any external dependencies in tests
class DatabaseMock: DatabaseType {
var itemsToReturn: [User] = []
var userToReturn: User?
func getItems(withPredicate predicate: String) -> [User] {
return itemsToReturn
}
func getItem(withPredicate predicate: String) -> User? {
return userToReturn
}
}
// actual test case
class UserManagerTests: XCTestCase {
var database: DatabaseMock = DatabaseMock()
var manager: UserManager!
// setup before each test
func setUp() {
database = DatabaseMock()
manager = UserManager(database: database)
}
func testReturnsCorrectUserForId() {
let expectedUser = User(id: "123")
database.userToReturn = expectedUser
let actualUser = manager.getUser(withId: "123")
XCTAssertNotNil(actualUser)
XCTAsseerEqual(actualUser.id, expectedUser.id)
}
// other tests ...
}
This is not the best example (because UserManager is just a wrapper around database) but I hope you have got some ideas of how write unit tests
You should think about WHAT you test.
You just test in your UnitTest the Json encoder. But i think the json encoder is still good tested and you not need to test it again.
You should test business logic (methods/functions) you have written and cover the code with the test