iOS Swift单元测试中如何处理无初始化器的Objective-C只读属性类?
Hey there! I’ve run into this exact scenario a few times when bridging Objective-C code into Swift test suites—let’s walk through your options, depending on whether you need a real instance or can get away with a mock.
Option 1: Use Objective-C Runtime to Create a Real Instance & Set Read-Only Properties
Since Objective-C is runtime-flexible, you can bypass the lack of a public initializer and modify read-only properties directly. Here’s how to do it in Swift:
First, make sure you’ve imported the ObjectiveC framework to access runtime APIs. Then:
import ObjectiveC import YourObjectiveCModule // Replace with your actual module containing ClassA func createTestableClassAInstance(with propertyValue: String) -> ClassA { // Allocate memory for the instance (works for NSObject subclasses) guard let instance = class_createInstance(ClassA.self, 0) as? ClassA else { fatalError("Failed to create instance of ClassA") } // Set the read-only property by targeting its underlying instance variable // Note: Most Objective-C read-only properties use an ivar prefixed with an underscore (e.g., _readonlyProperty) if let ivar = class_getInstanceVariable(ClassA.self, "_readonlyProperty") { object_setIvar(instance, ivar, propertyValue as NSString) // Match the property's type here } return instance }
- Important: This relies on knowing the name of the underlying instance variable (usually
_propertyNamefor a property namedpropertyName). If you don’t have access to the ClassA source, you can useclass_copyIvarListto list all ivars and find the right one. - This gives you a real instance of ClassA, which is useful if your test depends on the actual behavior of ClassA (not just its interface).
Option 2: Mock the Class (Cleaner for Dependency Testing)
If you’re testing a class that depends on ClassA (rather than testing ClassA itself), mocking is almost always the better choice—it isolates your test from ClassA’s internal logic. For Objective-C classes, OCMock is the go-to tool here:
First, add OCMock to your test target via CocoaPods or Swift Package Manager. Then:
import OCMock import YourObjectiveCModule func testYourDependentClass() { // Create a mock of ClassA let mockClassA = OCMClassMock(ClassA.self) // Stub the read-only property to return your desired value OCMStub(mockClassA.readonlyProperty).andReturn("Test Value") // Pass the mock to your dependent class/method under test let systemUnderTest = DependentClass(dependency: mockClassA) // Run your assertions... }
- OCMock lets you stub properties, methods, and even verify interactions (e.g., if a method on ClassA was called).
- If you prefer Swift-native mocking, frameworks like Cuckoo work too, but OCMock tends to be more seamless with Objective-C classes.
Which Should You Choose?
- Use the runtime approach only if you absolutely need a real instance of ClassA (e.g., testing ClassA’s own logic).
- Use mocking for all other cases—it keeps your tests fast, focused, and decoupled from external dependencies.
内容的提问来源于stack exchange,提问作者TomKo1

