# Basics of Unit Testing
A Unit test is very small function that you write to test some part of your code.(Unit tests are automated tests that run and validate a piece of code (known as the “unit”) to make sure it behaves as intended and meets its design.)
Unit tests have their own target in Xcode and are written using the XCTest framework. A subclass of XCTestCase contains test methods to run in which only the methods starting with “test” will be parsed by Xcode and available to run.
Unit Test - Testing isolated small pieces of code with Fake or Mock dependencies
Integration Test - App code is tested without faking database or HTTP connections
UI Test - Automated tests to test User Interface Interaction and Behavior
/// A simple struct containing a list of users.
struct UsersViewModel {
let users: [String]
var hasUsers: Bool {
return !users.isEmpty
}
}
/// A test case to validate our logic inside the `UsersViewModel`.
final class UsersViewModelTests: XCTestCase {
/// It should correctly reflect whether it has users.
func testHasUsers() {
let viewModel = UsersViewModel(users: ["Antoine", "Jaap", "Lady"])
XCTAssertTrue(viewModel.hasUsers)
}
}
# Test-Driven Development Life Cycle
Red: Write Unit test that fails
Green: Write App code to make Unit test pass
Refactor: Clean up and Imporve Unit test and app code
Repeat: Repeat these steps until all your app features are built and tested
# Unit Test Best Practices
Fast: Unit tests run fast
Independent: Unit tests are independent
Repeatable: Unit tests are repeatable
Self-Validating: Unit test validates itself
Thorough & Timely: Cover edge cases
We should not try to make our unit test test mulitple functions in the single unit test method.
Our test code is just as important as our application code
Although it’s a target for plenty, 100% coverage should not be your main goal when writing tests. Make sure to test at least your most important business logic at first as this is already a great start. Reaching 100% can be quite time consuming while the benefits are not always that big. In fact, it might take a lot of effort to even reach 100%.
Write a test before fixing a bug
# Life Cycle
override class func setUp()
override func setUpWithError()
test methods (addTeardownBlock is called when test method ends)
override func tearDownWithError()
overrride class func tearDown()
# Unit Test Method Requirements
func testColorIsRed() {
// Some code here
}
- Each test method must begin with the prefix test
- Test method does not take arguments
- Test method does not return a value
# Pattern
func test<System Under Test>_<Condition Or State Change>_<Expected Result>() {
// Some code here
}
# Example
func testSignupFormModel_WhenInformationProvided_passwordShouldMatchRepeatPassword() {
// Some code here
}
# Code Coverage
Apple Archive (opens new window)
Zedd - Code Coverage (opens new window)
# Test Method Code Structure; Arrange, Act, Assert
func testSignupFormModel_WhenInformationProvided_PasswordsShouldMatch() {
// Arrange
let firstName = "ted"
let email = "test@test.com"
let password = "1234567"
let sut = SignupFormModel(firstName: firstName, email: email, password: password)
// Act
let passwordsMatch = sut.doPasswordsMatch()
// Assert
XCTAssertTrue(passwordsMatch, "Expected True value for 'passwordsMatch but apparently it is FALSE", file: "SignupFormModelTests.swift", line: 36)
}
# Assertions
XCTAssertTrue(expression, "optional description") - Asserts that an expression is true
XCTAssertFalse(expression, "optional description") - Asserts that an expression is false
XCTAssertNil(expression, "optional description") - Asserts that an expression is nil
XCTAssertNotNil(expression, "optional description") - Asserts that an expression is not nil
XCTUnwrap - Asserts that an expression is not nil and returns the unwrapped value.
XCTAssertEqual(expression1, expression2, "optional description")
XCTAssertNotEqual(expression1, expression2, "optional description")
XCTAssertGreaterThan(expression1, expression2, "optional description")
XCTAssertLessThan(expression1, expression2, "optional description")
XCTAssertLessThanOrEqual(expression1, expression2, "optional description")
XCTAssertEqualWithAccuracy(expression1, expression2, accuracy, "optional description") - Asserts that two expressions have the same value within a certain accuracy.
XCTFail("optional description") - Generates a failure immediately and unconditionally.
XCTAssertThrowsError(expression, "optional description") - Asserts that an expression throws an error
XCTAssertNoThrow(expression, "optional description") - Asserts that an expression does not throw an error
# Example
import XCTest
@testable import unit_test
class SignupFormModelValidatorTests: XCTestCase {
var sut: SignupFormModelValidator!
override func setUp() {
sut = SignupFormModelValidator()
}
override func tearDown() {
sut = nil
}
func testSignFormModelValidator_WhenValidFirstNameProvided_ShouldReturnTrue() {
// Arrange
// Act
let isFirstNameValid = sut.isFirstNameValid(firstName: "Ted")
// Assert
XCTAssertTrue(isFirstNameValid, "The isFirstNameValid() Should Have returned TRUE for a valid first name but returned FALSE")
}
func testSignupFormModelValidator_WhenTooShortFirstNameProvided_ShouldReturnFalse() {
// Arrange
// Act
let isFirstNameValid = sut.isFirstNameValid(firstName: "a")
// Assert
XCTAssertFalse(isFirstNameValid, "The isFirstNameValid() Should Have returned False for a first name that is shorter than \(SignupConstants.firstNameMinLength) characters but it has returned TRUE")
}
func testSignupForModelValidator_WhenTooLongFirstNameProvided_ShouldReturnFalse() {
// Arrange
// Act
let isFirstNameValid = sut.isFirstNameValid(firstName: "TedTedTedTedTedTed")
XCTAssertFalse(isFirstNameValid, "The isFirstNameValid() Should Have returned False for a first name that is longer than \(SignupConstants.firstNameMaxLength) characters but it has returned TRUE")
}
}