Unit Testing of private methods in Xcode

14,037

Solution 1

How do i turn off these warnings in Xcode?

Don't.

Is there something else I could do to turn off these warnings?

Don't.

Am I doing something wrong in trying 'white box' testing?

No.

The solution is to move your private methods to a category in its own header. Import this header into both the real class and test-case class implementation files.

Solution 2

Remember that there's actually no such thing as "private methods" in Objective-C, and it's not just because it's a dynamic language. By design, Objective-C has visibility modifiers for ivars, but not for methods — it's not by accident that you can call any method you like.

@Peter's suggestion is a great one. To complement his answer, an alternative I've used (when I don't want/need a header just for private methods) is to declare a category in the unit test file itself. (I use @interface MyClass (Test) as the name.) This is a great way to add methods that would be unnecessary bloat in the release code, such as for accessing ivars that the class under test has access to. (This is obviously less of an issue when properties are used.)

I've found this approach makes it easy to expose and verify internal state, as well as adding test-only methods. For example, in this unit test file, I wrote an -isValid method for verifying correctness of a binary heap. In production, this method would be a waste of space, since I assume a heap is valid — I only care about it when testing for unit test regressions if I modify the code.

Solution 3

Looks like another question has the answer: Is there a way to suppress warnings in Xcode?

Solution 4

I was dealing with the same issue when I started with TDD few days ago. I've found this very interesting point of view in Test-Driven iOS Development book:

I have often been asked, “Should I test my private methods?” or the related question “How should I test my private methods?” People asking the second question have assumed that the answer to the first is “Yes” and are now looking for a way to expose their classes’ private interfaces in their test suites.

My answer relies on observation of a subtle fact: You already have tested your private methods. By following the red–green–refactor approach common in test-driven development, you designed your objects’ public APIs to do the work those objects need to do. With that work specified by the tests—and the continued execution of the tests assuring you that you haven’t broken anything—you are free to organize the internal plumbing of your classes as you see fit.

Your private methods are already tested because all you’re doing is refactoring behavior that you already have tests for. You should never end up in a situation where a private method is untested or incompletely tested, because you create them only when you see an opportunity to clean up the implementation of public methods. This ensures that the private methods exist only to support the class’s that they must be invoked during testing because they are definitely being called from public methods.

Solution 5

While having a private header or defining your own category are probably more correct solutions there is also another very simple solution: cast the object to (id) before calling the method.

Share:
14,037
Abizern
Author by

Abizern

Contract iOS and macOS developer in London. I organise my local NSCoder Night, and blog intermittently about my DVCS of choice at 365Git You can catch me on Twitter @abizern. You could even look at my profile at abizern.org which has more about me as well as my contact details. Sometimes I get snarky in my answers and comments. I'm not insulting you or laughing at you, I'm just trying to get you to think about your question or answer..

Updated on June 06, 2022

Comments

  • Abizern
    Abizern about 2 years

    I'm trying out test driven development in a toy project. I can get the tests working for the public interface to my classes (although I'm still on the fence because I'm writing more testing code than there is in the methods being tested).

    I tend to use a lot of private methods becuase I like to keep the public interfaces clean; however, I'd still like to use tests on these methods.

    Since Cocoa is a dynamic language, I can still call these private methods, but i get warnings in my tests that my class may not respond to these methods (although it clearly does). Since I like to compile with no warnings here are my questions:

    1. How do i turn off these warnings in Xcode?
    2. Is there something else I could do to turn off these warnings?
    3. Am I doing something wrong in trying 'white box' testing?
  • Quinn Taylor
    Quinn Taylor almost 15 years
    +1 — Great suggestion! For a little further detail, check out this question/answer: stackoverflow.com/questions/1020070/#1020330 The private header containing the category with private methods can be named MyClass_Private.h, for example.
  • Quinn Taylor
    Quinn Taylor almost 15 years
    That has part of the answer, but the best (or at least a more complete) solution for a particular case is probably related to either Peter's answer or my answer.
  • Abizern
    Abizern almost 15 years
    Thanks for this answer. I'm still not sure about it, but I must admit that there is a 'fun' element of writing a test and then making it pass.
  • Bach
    Bach over 13 years
    Great suggestion! I like this idea better, keeps the exposure of the methods in context.
  • Dan Rosenstark
    Dan Rosenstark over 11 years
    Wow, Peter Hosey should fix his answer to refer to yours. Great work, trying this now +1
  • Michael
    Michael almost 11 years
    I would say this is preferable. No duplication, no fragmentation of source files and takes advantage of the dynamic nature of Objective-C.
  • Erik Engheim
    Erik Engheim almost 11 years
    @Michael a problem with this approach in xCode5 is that using an unknown selector will cause a warning. The compiler has to see the selector somehow when compiling a particular unit test. For this reason I like Quinn Taylor's solution.
  • ArdenDev
    ArdenDev over 10 years
    I've declared these private methods in an extension header file and for some reason, in XCode5 the compiler complains when calling these private methods declared in the extension header. Any thoughts?
  • Nico
    Nico over 10 years
    @IphoneDeveloper: You should ask a separate question with more details.
  • Pétur Ingi Egilsson
    Pétur Ingi Egilsson over 10 years
    Please do not post links here, rather keywords. The link to apple has been moved.
  • Stunner
    Stunner over 10 years
    +1 for a helpful post, however I would like to point out that the unit test file you linked to is a broken link.
  • Eren Beşel
    Eren Beşel over 10 years
    A quick google search links to his git repo,
  • Abdalrahman Shatou
    Abdalrahman Shatou over 10 years
    I read this book and although he is right at some point, he doesn't answer the question. Why shall I move an IBAction to the interface file (it shall be private to other classes)? This is not a model object, we are talking about a controller here, a view controller in particular (this book doesn't talk enough about UI testing on iOS)
  • Alois Holub
    Alois Holub over 10 years
    My post is a reaction to the original question. I thought that the citation contained interesting point of view, because when you need to test private methods, it probably means that you should move those methods to the separate object. And this "leading" of the programmer to the smaller objects with shorter methods is the main benefit of the TDD. Of course, unit testing is not about UI testing. There are other tools (in instruments) and other tests for that.
  • atxe
    atxe about 10 years
    @QuinnTaylor Actually the convention is really helpful with the shortcut to switch between <class_name>.h & <class_name>.m (CMD+CTRL+UP_ARROW) cos' it also includes the <class_name>_Private.h :) At least in XCode5.
  • Rambatino
    Rambatino over 9 years
    This is also not the place to put duplicate links.