Until recently, I would have just pointed you at plenty of other existing questions that say that you generally shouldn't need access to any private stuff, in order to write unit tests - as the whole point of tests is to test the public (and protected) interface.
For example, in the question how to unit-test private methods in jquery plugins?, there is this answer:
The same applies here as with any other language and testing privates: To test private methods, you should exercise them via the public interface. In other words, by calling your public methods, the private methods get tested in the process because the public methods rely on the privates.
Generally private methods are not tested separately from the public interface - the entire point is that they are implementation details, and tests should generally not know too much about the specifics of the implementation
In your specific case, you say:
Since the data is private, I can only add things via the public interface of the object. This run codes that need not be run during a unit test and in some case is just a copy and paste from another test.
I would be cautious about having any tests that avoided code that normally needs to be run - for fear that your tests would spuriously pass, due to some difference between the test behaviour and the real world.
Having said all that, I recently learned about a really excellent article on writing testsuite called The Way of Testivus. It's a PDF that's only 12 pages long, and a really easy and enjoyable read.
What's relevant here is the section entitled "Sometimes, the test justifies the means" - in the context of violating encapsulation to enable testing. I highly recommend it.
(Another favourite of mine is "An ugly test is better than no test.")
So, in your context, if you still decide you really want a different way to set up data purely for tests, it might be reasonable to add a public setter method, specifically documented as only existing to aid writing of tests.
I'm going to use Python-like pseudocode based around your test cases to (hopefully) help explain this answer a little, rather than just expositing. But in short: This answer is basically just an example of small, single-function-level dependency injection / dependency inversion, instead of applying the principle to an entire class/object.
Right now, it sounds like your tests are structured as such:
def test_3_minute_confirm():
req = make_request()
sleep(3 * 60) # Baaaad
assertTrue(confirm_request(req))
With implementations something along the lines of:
def make_request():
return { 'request_time': datetime.now(), }
def confirm_request(req):
return req['request_time'] >= (datetime.now() - timedelta(minutes=5))
Instead, try forgetting about "now" and tell both functions what time to use. If it's most of the time going to be "now", you can do one of two main things to keep the calling code simple:
- Make time an optional argument that defaults to "now". In Python (and PHP, now that I reread the question) this is simple - default "time" to
None
(null
in PHP) and set it to "now" if no value was given.
- In other languages without default arguments (or in PHP if it becomes unwieldy), it might be easier to pull the guts of the functions out into a helper that does get tested.
For example, the two functions above rewritten in the second version might look like this:
def make_request():
return make_request_at(datetime.now())
def make_request_at(when):
return { 'request_time': when, }
def confirm_request(req):
return confirm_request_at(req, datetime.now())
def confirm_request_at(req, check_time):
return req['request_time'] >= (check_time - timedelta(minutes=5))
This way, existing parts of the code don't need to be modified to pass in "now", while the parts you actually want to test can be tested much more easily:
def test_3_minute_confirm():
current_time = datetime.now()
later_time = current_time + timedelta(minutes=3)
req = make_request_at(current_time)
assertTrue(confirm_request_at(req, later_time))
It also creates additional flexibility in the future, so less needs to change if the important parts need to be reused. For two examples that pop into mind: A queue where requests might need to be created moments too late but still use the correct time, or confirmations have to happen to past requests as part of data sanity checks.
Technically, thinking ahead like this might violate YAGNI, but we're not actually building for those theoretical cases - this change would just be for easier testing, that just happens to support those theoretical cases.
Is there any reason you would recommend one of these solutions over another?
Personally, I try to avoid any sort of mocking as much as possible, and prefer pure functions as above. This way, the code being tested is identical to the code being run outside of tests, instead of having important parts replaced with mocks.
We've had one or two regressions that no one noticed for a good month because that particular part of the code was not often tested in development (we weren't actively working on it), was released slightly broken (so there were no bug reports), and the mocks used were hiding a bug in the interaction between helper functions. A few slight modifications so mocks weren't necessary and a whole lot more could be easily tested, including fixing the tests around that regression.
Change the system date and time for the test, using exec(). This can work out if you are not afraid to mess up with other things on the server.
This is the one that should be avoided at all costs for unit testing. There's really no way of knowing what kinds of inconsistencies or race conditions might be introduced to cause intermittent failures (for unrelated applications, or co-workers on the same development box), and a failed/errored test might not set the clock back correctly.
Best Answer
What test framework are you using? Looks similar to junit (
ExpectedException
,MatchType
), but the square bracket syntax is new to me. Is this c#'s NUNIT?I use junit 4's ExpectedException in a programatic way. Therefore, I can set a lot of attributes of the Exception, like
thrown.expectMessage(startsWith("What"));
. For me, this is sufficiently flexible.That does not seem to be good idea. With that argument, you could never use a general exception. But abstraction is good, to cope with complexity. In your case, thousands of new Exception classes, that also have to be caught and handled somewhere, would produce the complexity that can better be dealt with via more abstract exceptions, like
ArgumentException
.