15

In my custom class WLNetworkClient I had to implement such method:

required init(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}

I do not need to use that, but I would like to test this to make 100% code coverage. Do you know how to achieve this?

I tried following way with no success:

let nc = WLNetworkClient(coder: NSCoder())
XCTAssertNotNil(nc)
Bartłomiej Semańczyk
  • 59,234
  • 49
  • 233
  • 358

6 Answers6

15

Production code:

required init?(coder: NSCoder) {
    return nil
}

Test:

func testInitWithCoder() {
    let archiverData = NSMutableData()
    let archiver = NSKeyedArchiver(forWritingWithMutableData: archiverData)
    let someView = SomeView(coder: archiver)
    XCTAssertNil(someView)
}

Since the required initializer returns nil and does not use the coder, the above code can be simplified to:

func testInitWithCoder() {
    let someView = SomeView(coder: NSCoder())
    XCTAssertNil(someView)
}
Eneko Alonso
  • 18,884
  • 9
  • 62
  • 84
Rudolf Adamkovič
  • 31,030
  • 13
  • 103
  • 118
  • This doesn't work for me in a slightly different situation. I'm calling super.init(coder: aDecoder) as the second line and it stops running on that line. No crash just doesn't finish the test. – Jeremiah Jan 30 '18 at 21:52
  • 3
    Fails for me (Xcode 9.3, Swift 4.1): `test failure: testInitWithCoder() failed: failed: caught "NSInvalidArgumentException", "*** -decodeObjectForKey: only defined for abstract class. Define -[NSKeyedArchiver decodeObjectForKey:]!" ` – Oliver Pearmain Apr 10 '18 at 13:41
  • @Oliver, when I got that error, it was because I was feeding my initialiser a dedicated archiver in place of a dedicated DEarchiver... I should have created a new NSKeyedUnarchiver with the archiverData as init param. – Liz Jun 05 '18 at 10:13
  • The initializer `NSKeyedArchiver(forWritingWith:)` has been deprecated in iOS12. If you're getting that warning, you can replace the line with `let archiver = NSKeyedArchiver(requiringSecureCoding: true)` – Martin Nov 19 '18 at 07:05
  • This wont work now, Fail with error `caught "NSInvalidArgumentException", "*** -containsValueForKey: cannot be sent to an abstract object of class NSCoder: Create a concrete instance!"` – Saif Feb 06 '20 at 07:33
3

Here is answer which should help you:

let cd = NSKeyedUnarchiver(forReadingWithData: NSMutableData())
let c = CustomTextField(coder:cd)
Community
  • 1
  • 1
Patrick
  • 41
  • 3
  • 3
    Unfortunalety it stilll doesnt work: `*** -[NSKeyedUnarchiver initForReadingWithData:]: data is empty; did you forget to send -finishEncoding to the NSKeyedArchiver?` – Bartłomiej Semańczyk Dec 22 '15 at 09:48
1

Answer from Rudolf Adamkovič ist still working with Swift 4:

required init?(coder aDecoder: NSCoder) {
    return nil
}

func testInitWithCoder() {
    // 1. Arrange
    let archiver = NSKeyedArchiver(forWritingWith: NSMutableData())

    // 2. Action
    let viewController = ViewController(coder: archiver)

    // 3. Assert
    XCTAssertNil(viewController)
}
sundance
  • 2,930
  • 1
  • 20
  • 25
1

I combined Rudolf's answer with Liz's suggestion and ended with the following solution:

let viewController = SomeTableViewController(
    presenter: SomePresenterMock(),
    coder: NSKeyedUnarchiver(forReadingWith: Data())) 

XCTAssert(viewController?.tableView.numberOfSections == 1)

The key here is to use NSKeyedUnarchiver(forReadingWith: Data()) as mock coder, otherwise the test crashes with NSInvalidArgumentException.

1

I use improved Rudolf's Adamkovič answer:

required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
}

func test_initWithCoder() {
    let object = SomeView()
    let data = NSKeyedArchiver.archivedData(withRootObject: object)
    let coder = NSKeyedUnarchiver(forReadingWith: data)
    let sut = SomeView(coder: coder)
    XCTAssertNotNil(sut)
}
ChikabuZ
  • 10,031
  • 5
  • 63
  • 86
1

It seems that all the previous answers stopped working after Swift 5.

I managed to modify @ChikabuZ answer and make it work like this:

func testInitWithCoder() {
    // Given
    let object = SomeView()
    let data = try! NSKeyedArchiver.archivedData(withRootObject: object, requiringSecureCoding: false)
    let coder = try! NSKeyedUnarchiver(forReadingFrom: data)

    // When
    let sut = SomeView(coder: coder)

    // Then
    XCTAssertNotNil(sut)
}