Bird
Raised Fist0
iOS Swiftmobile~20 mins

Unit testing ViewModels in iOS Swift - Practice Problems & Coding Challenges

Choose your learning style10 modes available
Challenge - 5 Problems
🎖️
ViewModel Testing Master
Get all challenges correct to earn this badge!
Test your skills under time pressure!
❓ ui_behavior
intermediate
2:00remaining
How does ViewModel update UI state?
Consider a ViewModel that updates a published property to change the UI. What will the UI show after calling loadData() if the data fetch succeeds?
iOS Swift
class SampleViewModel: ObservableObject {
  @Published var message = ""
  func loadData() {
    message = "Loading..."
    DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
      self.message = "Data loaded"
    }
  }
}

let vm = SampleViewModel()
vm.loadData()
// After 2 seconds, what is vm.message?
A"Loading..."
B""
C"Data loaded"
Dnil
Attempts:
2 left
💡 Hint
Think about asynchronous updates and when the message changes.
❓ lifecycle
intermediate
1:30remaining
When is ViewModel deinitialized?
Given this ViewModel and usage, when will the deinit print statement run?
iOS Swift
class MyViewModel {
  deinit {
    print("ViewModel deinitialized")
  }
}

var vm: MyViewModel? = MyViewModel()
vm = nil
// When does "ViewModel deinitialized" print?
AImmediately after setting vm = nil
BNever, because vm is a strong reference
CAfter a delay of 5 seconds
DOnly when the app closes
Attempts:
2 left
💡 Hint
Think about reference counting and when objects are freed.
🔧 Debug
advanced
2:30remaining
Why does this ViewModel test fail?
This unit test expects the ViewModel's isLoading to be false after fetchData(). Why does it fail?
iOS Swift
class TestViewModel {
  var isLoading = false
  func fetchData() {
    isLoading = true
    DispatchQueue.global().async {
      sleep(1)
      self.isLoading = false
    }
  }
}

let vm = TestViewModel()
vm.fetchData()
assert(vm.isLoading == false)  // Fails here
ABecause isLoading is set to false asynchronously after the assert runs
BBecause isLoading is never set to true
CBecause sleep(1) blocks the main thread
DBecause assert syntax is incorrect
Attempts:
2 left
💡 Hint
Consider when the assert runs compared to the async update.
🧠 Conceptual
advanced
1:30remaining
What is the main benefit of unit testing ViewModels?
Why do developers write unit tests specifically for ViewModels in an iOS app?
ATo test UI animations and transitions
BTo verify business logic without UI dependencies
CTo check network connectivity directly
DTo improve app launch speed
Attempts:
2 left
💡 Hint
Think about separation of concerns in app architecture.
📝 Syntax
expert
2:00remaining
What error does this ViewModel test code produce?
What error occurs when running this Swift unit test code?
iOS Swift
class VM {
  var count = 0
  func increment() {
    count += 1
  }
}

func testIncrement() {
  let vm: VM? = VM()
  vm.increment()
  assert(vm?.count == 1)
}
testIncrement()
ACompile-time error: Missing return in function
BRuntime error: nil unwrapping
CTest passes with no error
DCompile-time error: Cannot call method on optional without unwrapping
Attempts:
2 left
💡 Hint
Look at how vm is declared and how increment() is called.

Practice

(1/5)
1. What is the main purpose of unit testing a ViewModel in iOS development?
easy
A. To verify the app's business logic without involving the user interface
B. To test the app's user interface elements visually
C. To check the app's network connectivity
D. To measure the app's performance speed

Solution

  1. Step 1: Understand the role of ViewModel

    The ViewModel handles app logic and data preparation, separate from UI.
  2. Step 2: Identify unit testing focus

    Unit testing targets logic correctness without UI involvement.
  3. Final Answer:

    To verify the app's business logic without involving the user interface -> Option A
  4. Quick Check:

    Unit testing ViewModel = test logic only [OK]
Hint: Unit tests check logic, not UI or performance [OK]
Common Mistakes:
  • Confusing unit tests with UI tests
  • Thinking unit tests check network or performance
  • Assuming ViewModel tests require UI elements
2. Which XCTest method is used to prepare the environment before each test runs in a ViewModel test class?
easy
A. setUp()
B. runTest()
C. testExample()
D. tearDown()

Solution

  1. Step 1: Recall XCTest lifecycle methods

    setUp() runs before each test to prepare needed objects.
  2. Step 2: Differentiate from tearDown()

    tearDown() cleans up after each test, not before.
  3. Final Answer:

    setUp() -> Option A
  4. Quick Check:

    Prepare tests before running = setUp() [OK]
Hint: setUp() runs before each test to prepare environment [OK]
Common Mistakes:
  • Using tearDown() to prepare tests instead of clean up
  • Confusing testExample() as a setup method
  • Assuming runTest() is a XCTest lifecycle method
3. Given this ViewModel test code snippet, what will be the value of result after the test runs?
class CounterViewModel {
  var count = 0
  func increment() { count += 1 }
}

func testIncrement() {
  let vm = CounterViewModel()
  vm.increment()
  let result = vm.count
}
medium
A. Compilation error
B. 0
C. nil
D. 1

Solution

  1. Step 1: Analyze initial state

    CounterViewModel starts with count = 0.
  2. Step 2: Apply increment method

    increment() adds 1 to count, so count becomes 1.
  3. Final Answer:

    1 -> Option D
  4. Quick Check:

    Increment once = count 1 [OK]
Hint: Increment adds 1, so count becomes 1 [OK]
Common Mistakes:
  • Assuming count stays 0 without increment
  • Thinking result is nil because of missing return
  • Expecting compilation error due to missing test annotation
4. Identify the error in this ViewModel unit test code:
class MyViewModel {
  var value = 0
  func updateValue() { value = 10 }
}

func testUpdateValue() {
  let vm = MyViewModel()
  vm.updateValue()
  XCTAssertEqual(vm.value, 0)
}
medium
A. updateValue() method is missing return type
B. XCTAssertEqual expects 10 but checks for 0
C. MyViewModel class is not marked as final
D. vm instance is not initialized properly

Solution

  1. Step 1: Check updateValue() effect

    updateValue() sets value to 10, so vm.value should be 10.
  2. Step 2: Compare XCTAssertEqual parameters

    Test expects vm.value to be 0, which is incorrect after updateValue().
  3. Final Answer:

    XCTAssertEqual expects 10 but checks for 0 -> Option B
  4. Quick Check:

    Test expected value must match updated value [OK]
Hint: Check expected vs actual values in XCTAssertEqual [OK]
Common Mistakes:
  • Ignoring the expected value in XCTAssertEqual
  • Thinking missing return type causes error here
  • Assuming class must be final for testing
5. You want to test a ViewModel method that fetches user data asynchronously and updates a userName property. Which approach correctly tests this behavior using XCTest?
hard
A. Call the method, then immediately assert userName without waiting
B. Test only synchronous methods; skip async ones
C. Use XCTestExpectation to wait for the async update before asserting userName
D. Mock the ViewModel and do not call the real method

Solution

  1. Step 1: Understand async testing need

    Async methods update properties after delay, so test must wait.
  2. Step 2: Use XCTestExpectation

    Expectation waits for async completion before asserting userName value.
  3. Final Answer:

    Use XCTestExpectation to wait for the async update before asserting userName -> Option C
  4. Quick Check:

    Async test = use XCTestExpectation [OK]
Hint: Wait for async calls with XCTestExpectation before assertions [OK]
Common Mistakes:
  • Asserting async results immediately without waiting
  • Skipping async tests thinking they are too complex
  • Mocking ViewModel without testing real async behavior
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard