UserDefaults Unit testing in iOS

Gagan Vishal Mishra
3 min readFeb 26, 2022

In this example, we’ll try to write a unit test case of UserDefaults in iOS.

Suppose that we are in a situation where we are saving some product IDs in user default for a shopping app. When the user moves an item into the cart, we save an array of IDs in UserDefaults and if the user removes an item from the cart then we fetch and update the saved IDs array. Later we save the updated IDs array in UserDefaults.

We can write an extension (UserDefaults+Extension.swift)of UserDefaults as below

import Foundationprotocol UserDefaultKey {
var userDefaulKey: String {get set}
}
extension UserDefaults: UserDefaultKey {
var userDefaulKey: String {
get {
Keys.cartItemIDS
} set {
Keys.cartItemIDS = newValue
}
}
private enum Keys {
static var cartItemIDS = "cartItemIDs"
}
class var savedCartItems: [Int]? {
get {
return UserDefaults.standard.value(forKey: Keys.cartItemIDS) as? [Int]
} set {
UserDefaults.standard.set(newValue, forKey: Keys.cartItemIDS)
}
}
}

Now we have an extension of UserDefaults which confirms a protocol UserDefaultKey. Now in real-world, we’ll save an id as below

func manageProductInCart(product: MainProductModel) {
// get all saved product ids and store in an array
var availaleCartItemIDs = UserDefaults.savedCartItems ?? []
// if in case product is already available in cart, then dont add item in array. MainProductModel has a prperty id
guard availaleCartItemIDs.firstIndex(where: {$0 == product.id}) == nil else {
print("Item already available in cart. Retunr from here")
return
}
availaleCartItemIDs.append(product.id)
// save in user deafults. This will update the array of IDs saved in UserDefaults
UserDefaults.savedCartItems = availaleCartItemIDs
}

Now let's come to the UnitTesting part

class UserDefaultsTest: XCTestCase {// Create a KEY for Unit test 
private var userDefaultTestKey = "userDefaultsTestKeyCart"
var cartViewModel: ProductViewModelProtocol!
override func setUp() {
UserDefaults.standard.userDefaulKey = userDefaultTestKey
let someFakeCartModel = ... Create a fake cart model
cartViewModel = CartListViewModel(model: someFakeCartModel)
}
override func tearDown(){
UserDefaults.standard.removeObject(forKey: userDefaultTestKey)
}
func testWhenItemRemovedFromCart() {
UserDefaults.savedCartItems = getFakeStoredCartProductIDs() // 1. Get some data from your fake
let beforeRemoving = UserDefaults.savedCartItems // 2. Store items in an array
let indexPath = IndexPath(row: 0, section: 0) // get an item from given indexpath. We are displying all products in UICollectionView.
let prouct = cartViewModel.product(at: indexPath) // Get a product from given indexpath from your ViewModel
cartViewModel.removeACartProduct(from: prouct) //3. Remove an item from cart
let afterRemoving = UserDefaults.savedCartItems
XCTAssertTrue(beforeRemoving?.count != afterRemoving?.count, "Could not remove item from cart") // 4
}
}

Please have a look at userDefaultTestKey constant, in the Unit Test case we are using a different key and protocol UserDefaultKey allows us to do so.

in setUp() we are setting a new Key for UserDefaults and in tearDown() we are removing.

Here in the above code, have a look at steps 1, 2, 3 & 4.

In step 1, we are fetching storedIDs from a fake model.

In step 2, we are storing storedIDs an array named beforeRemoving.

In step 3, remove an item.

In step 4, we need to verify if the count is different or not. Here we can check for contains(object: ) too.

That’s it. Now you can test your UserDefaults without altering actual data saved in UserDefault.

Enjoy!!!

--

--

Gagan Vishal Mishra

Experienced Sr iOS and mobile app developer. Love to solve customer problem in the easiest and quickest way.