Skip to content
tezvyn:

Unit test a ViewModel with a mocked NetworkService

Source: interviewMediumHow cards are made

Summary

dependency injection and test isolation.

Key points

inject the protocol, supply a mock returning canned data, assert published state transitions.

Watch out for

hitting the real network or testing the concrete service instead of the ViewModel.

What's really being asked

This probes whether you understand testable architecture: depending on abstractions rather than concrete types so you can substitute behaviour in tests. The interviewer wants protocol-oriented dependency injection, not singletons reached for inside the ViewModel.

The full answer

The ViewModel should accept the NetworkService protocol through its initializer, defaulting to the real implementation in production. In the test you create a mock type conforming to the same protocol whose fetch method returns a predetermined success value, perhaps with a configurable stubbed result. You instantiate the ViewModel with that mock, invoke the action, and assert the observable state. If the ViewModel publishes via Combine, ObservableObject, or async sequences, you assert the loading flag flips on then off and the data property holds the expected decoded model.

The mistakes people make

Using URLProtocol stubbing when simple protocol mocking suffices, calling the real network and tolerating slowness, or constructing the concrete NetworkService inside the ViewModel which makes substitution impossible. Another mistake is asserting the mock was configured rather than verifying the ViewModel produced correct state.

What usually comes next

How would you test the failure path? How do you handle the async gap between calling fetch and the state updating? How would you verify the network method was called exactly once with the right arguments?

A concrete example

Define protocol NetworkServicing with func fetchUsers async throws returning Users. The production AppNetworkService conforms to it. In tests, MockNetworkService stores a stubbedUsers array and a callCount; its fetchUsers increments the count and returns the stub. The test creates ViewModel with the mock, awaits viewModel.loadUsers, then asserts viewModel.users equals the stub, viewModel.isLoading is false, and mock.callCount equals one. No real connection is opened, so the test is fast and deterministic.

Interview question

Why inject the NetworkService as a protocol rather than constructing it inside the ViewModel?

  • a.Protocols execute faster than concrete types at runtime
  • b.It lets tests substitute a mock so behaviour is deterministic and offlineCorrect
  • c.It makes the ViewModel smaller in compiled binary size
  • d.It is required by XCTest before any assertion can run
Why?

Protocol injection lets the test pass a controllable mock, keeping the test fast, offline, and deterministic. Binary size and runtime speed are unrelated, and XCTest imposes no such requirement.

Just read this? Test yourself on what you have been reading.

Read the original → donnywals.com

Put your scrolling time to good use

Learn one idea, try a quiz and save useful cards for revision. Tezvyn makes it easy to learn and stay current in your tech field, a few minutes at a time.

The iPhone app is on the way

We are building it. Until it lands, nothing here is held back from you: every interview card, your saved cards, streaks and the job board all work in Safari, plus hundreds of free practice quizzes of thirty questions each. Sign in and it all carries over to the app the day it arrives.

Want it as an icon? Tap Share at the bottom of Safari, then Add to Home Screen. It opens full screen and the cards you have read stay available offline.

Get it on Google PlayiPhone app coming soon

We are hiring for this. Open roles that interview on ios — each one lists the topics its interview covers.

See open roles