Unit Test of Private IBOutlet in UIViewController
Introduction
UIViewController is a key part of iOS application. If you implement a ViewController with storyboard/xib, you use @IBOutlet
attribute.
Which access level do you give those variables? public
, internal
or private
. I often use private
. It makes codes concealed and independent from outside. This is good, but may decrease testability of ViewController. Because private
properties are not accessible. Properties with internal
can be accessed by using @testable import
, but not for private
.
In this article, I’ll show small tips of testing private properties.
Mirror API
I introduce Mirror API.
Mirror: A representation of the substructure and display style of an instance of any type.
A mirror describes the parts that make up a particular instance, such as the instance’s stored properties, collection or tuple elements, or its active enumeration case.
The above description is quoted from Apple document. It shows we can get stored properties via mirror.
Let’s think the following type.
Create movie instance and set it to movie variable with Any
type.
Next, investigate what the type is
Of course, the type is Movie
and the instance is interpreted as struct
.
Mirror also enables us to inspect stored properties by children property.
Mirror.Children is just a typealias of AnyCollection<Mirror.Child>
and Mirror.Child is just a typealias of (label: String?, value: Any)
.
Try looping children
,
can get stored property information regardless of the access level even if it is private
. label
is the name of stored property variable and the value
is the instance of it.
Can access each property by this function.
The MirrorPath type appeared out of nowhere is a Swift protocol.
Do not declare new conformances to this protocol; they will not work as expected. 😱
But, don’t worry. String
and Int
are conformed to this protocol
So, can get instances of movie’s properties like the following.
Unit Test of Private IBOutlet in UIViewController
In the previous section, we knew can get private instances by Mirror. Using the tips, try testing private IBOutlet in UIViewController.
Target ViewController is the following. Layout is configured in Main.storyboard
.
In the test module, I defined this common MirrorObject
class.
Inheriting this class, define new class. Via this mirror instance, can access private titleLabel
in ViewController.
The variable name is used for Mirror.descendant
first parameter, so it must be same as the IBOutlet
variable name here.
Finally, can test private property of ViewController 🎉 🎉.
The method I described, accessing private property, can be used for application code, but it is risky. Only for test codes, would be helpful. IMO.