class RemoveMyDataConsentFormVCTests: XCTestCase {
var viewController: RemoveMyDataConsentFormVC!
override func setUp() {
super.setUp()
let storyboard = UIStoryboard(name: "RemoveMyDataConsentForm", bundle: nil) // Replace "Main" with your storyboard name
viewController = storyboard.instantiateViewController(withIdentifier: "RemoveMyDataConsentFormVC") as? RemoveMyDataConsentFormVC
viewController.loadViewIfNeeded()
}
override func tearDown() {
viewController = nil
super.tearDown()
}
func testSetupTextFields() {
if viewController.emailTextField.text == "" {
viewController.emailTextField.text = nil
}
XCTAssertNotNil(viewController.emailTextField.text, "Error for email should be hidden")
XCTAssertTrue((viewController.errorForStates != nil), "Error for email should be hidden")
XCTAssertTrue((viewController.errorForCountries != nil), "Error for email should be hidden")
XCTAssertTrue((viewController.errorForLastName != nil), "Error for email should be hidden")
XCTAssertTrue((viewController.errorForFirstName != nil), "Error for email should be hidden")
}
}
Above is my code for my UIViewController
for which I have written a unit test to setup text fields. I am looking for an example to improve the way I write unit tests using Swift 5.
The test is passing, but it didn’t seem to be a good practice to write the unit test the correct way.
There are two common types of tests:
We test UI behaviors (like UIViewController
subclasses) in UI tests. But when we write these tests, we generally would not reach in and examine the internal state of the view controller, but rather test what is presented in the UI.
For example, a UI test would supply some value to some UI control (like a text field), and then the UI test would verify that the UI correctly reflected the error. But we do not test the internal error objects, but rather how it was presented in the UI.
We use unit tests to test the behavior of individual methods, not user interfaces. So, we might write unit tests that verify that a given function returned the expected result.
For example, one might validate that some text string provided to some “validate string” function returned the correct error object.
To this end, many of us try to abstract the business logic out of the view layer (i.e., we pull it out of the view controller, for example). Different teams/projects use different techniques/terminology to accomplish this: Some use “view models”. Some use “presentation” objects. Some use more abstract “controller” (not to the confused with UI-specific “view controller”) or “service” objects to encapsulate this business logic.
Regardless of how one approaches this, the idea is that for testability reasons (as well as just general good design) we abstract business logic out of the UI layer of our codebase (i.e., we pull this out of the view controllers). We then write “unit tests” to validate these business logic functions/objects, completely separate from any UI code. We then write “UI tests” to verify that our UI behaves the way we expected, not to verify the internal state of the view layer.
See the distinction between unit tests a UI tests about 1:33 into WWDC 2023’s Fix failures faster with Xcode test reports: