How do you unit test a widget’s timeline logic when the provider’s Context cannot be created in a test?
Predict first
Can a test create a TimelineProviderContext?
import WidgetKit
let context = TimelineProviderContext()
- No: the compiler reports it has no accessible initializers
- Yes, with TimelineProviderContext(family: .systemSmall)
- Yes, but only on a device
Mental model. You cannot hire the building inspector to test your plumbing in a workshop, so you test the pipes (the pure function) and let the inspector (the system) check the finished install.
The provider’s methods receive a TimelineProviderContext (family, preview flag, size), and that type has no public initializer: the check shows the compiler refusing TimelineProviderContext() (“cannot be constructed because it has no accessible initializers”). So you cannot call getTimeline(in:completion:) from a test with a fabricated context, and you should not try to subclass or mock it. The design answer is to keep the provider a thin adapter: it reads the family and the data, then calls a pure function such as makeTimeline(now:data:family:) in a shared module, which returns a Timeline (a plain value you can inspect: entries, policy).
Test that function with injected now and data, including the failure and empty cases from the previous question, and snapshot-test the view for each entry with a fixed rendering mode. Then use Xcode’s widget previews and a device for what tests cannot see: the real budget, rendering modes and process limits. Link only the shared module into the test target, not the extension, so tests do not depend on extension entry points.
The type is not constructible outside WidgetKit.
Common traps
- Putting the logic in the provider method itself. The only thing the test can reach is a context it cannot construct.
They might follow up with
- How do I inject the clock? Pass
nowin as a parameter, as the fallback check does.
Related questions
- 19.4.1 How does a WidgetKit widget work: providers, timeline entries, reload policies, families and the refresh…
- 19.8.1 What should a timeline provider return when the fetch fails, and what do the reload policies mean as values?
- 19.7.2 How do you treat extensions, widgets and intents as a public API of the product: shared modules, data…
← What should a timeline provider return when the fetch fails, and what… How do several widgets live in one extension, and what is a widget’s… →