单元测试覆盖率低于预期,如何完善JSON转Data工具类测试?
Got it, let's walk through how to beef up your unit tests and fix a couple of small issues in your utility code to get better coverage and more reliable tests.
First, Refactor the Utility Method for Better Swift Compatibility
Your current method uses [String : AnyObject] as the parameter type, which is a holdover from Objective-C compatibility. In modern Swift, JSONSerialization works perfectly with [String : Any] (since JSON values can be strings, numbers, booleans, arrays, dictionaries, or nil), so let's update that to avoid unnecessary type casting:
static func data(from json: [String : Any]) throws -> Data { return try JSONSerialization.data(withJSONObject: json, options: .prettyPrinted) }
(Note: You don't need the extra do/catch block here—just rethrow the error directly. It keeps the code cleaner without changing behavior.)
Expand Test Cases to Cover All Scenarios
Right now your test only covers the happy path. To hit full coverage, you need to test failure scenarios, edge cases, and validate the correctness of the serialized data (not just that it exists).
1. Improve the Success Path Test
Instead of just checking that data isn't nil, verify that the serialized data can be deserialized back to match the original JSON. This ensures the serialization actually works as expected:
func testJsonToDataSuccessAndAccurate() { let testJson: [String : Any] = [ "greeting": "Hello, World!", "age": 30, "isActive": true, "hobbies": ["reading", "coding"], "contact": ["email": "test@example.com"] ] do { let data = try Utils.data(from: testJson) XCTAssertNotNil(data, "Serialized data should not be nil") // Deserialize the data back to JSON and validate matches original guard let deserializedJson = try JSONSerialization.jsonObject(with: data) as? [String: Any] else { XCTFail("Failed to deserialize data back to JSON") return } XCTAssertEqual(deserializedJson["greeting"] as? String, testJson["greeting"] as? String) XCTAssertEqual(deserializedJson["age"] as? Int, testJson["age"] as? Int) XCTAssertEqual(deserializedJson["isActive"] as? Bool, testJson["isActive"] as? Bool) XCTAssertEqual(deserializedJson["hobbies"] as? [String], testJson["hobbies"] as? [String]) XCTAssertEqual(deserializedJson["contact"] as? [String: String], testJson["contact"] as? [String: String]) } catch { XCTFail("Unexpected error during serialization: \(error.localizedDescription)") } }
2. Add Failure Scenario Tests
JSONSerialization throws errors when given non-serializable objects (like Date, custom classes, or cyclical references). Let's test these cases:
func testJsonToDataFailureWithInvalidObjects() { // Test with a non-serializable Date object let invalidJsonWithDate: [String: Any] = ["lastLogin": Date()] XCTAssertThrowsError(try Utils.data(from: invalidJsonWithDate)) { error in guard let jsonError = error as? NSError else { XCTFail("Expected JSONSerialization error, got \(type(of: error))") return } XCTAssertEqual(jsonError.code, JSONSerialization.ErrorCode.invalidJSONObject.rawValue) } // Test with cyclical reference (dictionary references itself) var cyclicalJson: [String: Any] = [:] cyclicalJson["selfReference"] = cyclicalJson XCTAssertThrowsError(try Utils.data(from: cyclicalJson)) }
3. Test Edge Cases
Don't forget edge cases like empty dictionaries, strings with special characters, or nil values:
func testJsonToDataEdgeCases() { // Empty dictionary let emptyJson: [String: Any] = [:] do { let data = try Utils.data(from: emptyJson) XCTAssertNotNil(data) let deserialized = try JSONSerialization.jsonObject(with: data) as? [String: Any] XCTAssertEqual(deserialized?.count, 0) } catch { XCTFail("Empty dictionary should serialize successfully") } // String with special characters let specialCharJson: [String: Any] = ["message": "Line 1\nLine 2 🌍 \\\"Quoted Text\\\""] do { let data = try Utils.data(from: specialCharJson) XCTAssertNotNil(data) guard let deserialized = try JSONSerialization.jsonObject(with: data) as? [String: Any], let message = deserialized["message"] as? String else { XCTFail("Failed to deserialize special character string") return } XCTAssertEqual(message, "Line 1\nLine 2 🌍 \\\"Quoted Text\\\"") } catch { XCTFail("Special character string should serialize successfully") } }
Why This Works
By expanding your tests to cover:
- The happy path (with validation of serialized content)
- Failure scenarios (invalid objects, cyclical references)
- Edge cases (empty data, special characters)
You'll hit 100% test coverage for your utility method, and your tests will catch more potential issues in the future. The refactor to [String: Any] also makes the method easier to use without type casting.
内容的提问来源于stack exchange,提问作者ridvankucuk

