Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit Testing: Logging and Dependency Injection

So regards logging from SO and other sites on the Internet the best response seems to be:

void DoSomething() {
    Logger.Log("Doing something!");
    // Code...
}

Now generally you'd avoid static methods but in the case of logging (a special case) this is the easiest and cleaniest route. Within the static class you can easily inject an instance via a config file/framework to give you the same effect as DI.

My problem comes from a unit testing perspective.

In the example code above imagine the point of DoSomething() was to add two numbers together. I'd write my unit tests for this fine. What about the logging?

Would I write a unit test for the logging (yet use a mock instance for the logger itself)? I know if this was the case I would have to write an integration test to prove the logger actually wrote to a log file but I'm not sure.

Following Test Driven Development (which I do) the unit test would be required for me to dictate the interface no?

Any advice?

like image 240
Finglas Avatar asked Jul 22 '09 20:07

Finglas


People also ask

What is unit testing using dependency injection?

Dependency injection allows unit testing, but it also allow modification of an object's behavior without altering the code of that object (open/closed principle). So, it isn't just testable code, but flexible code that results.

Should you use dependency injection in unit tests?

Unit testing is challenging when you add frameworks around your tests subjects. Dependency injection is one of those patterns supported by frameworks, that we usually need in any project.

Should logging be unit tested?

If you have utility methods that assist logging, you should unit test those. If you want to "test" logging, do it as part of integration testing and assert that some stream (usually console) contains certain output.

Why is dependency injection useful for testing?

Dependency injection helps to develop testable code, allowing developers to write unit tests easily. You can use mock databases with dependency injection, and test your application without affecting the actual database.


3 Answers

Personally, I practice TDD/BDD pretty religiously and I almost never test logging. With some exceptions logging is either a developer convenience or a usability factor, not part of the method's core specification. It also tends to have a MUCH higher rate of change than the actual semantics of the method, so you wind up breaking tests just because you added some more informational logging.

It's probably worthwhile to have some tests that simply exercise the logging subsystem, but for most apps I wouldn't test that each class uses the log in a particular way.

like image 188
Avdi Avatar answered Oct 20 '22 10:10

Avdi


I've only ever written a few unit tests for logging. It's a pain, either making the production code messy (due to injecting the logger) or the test smelly (replacing the static logger with a mock). Unlike McWafflestix, I've often not found it to be worth the effort afterwards.

How much do you really want to know whether the logging is working, beyond what you'll see through other (hands-on) testing? You might want to use DI for the occasional class where logging is really important, but otherwise I just wouldn't bother testing logging.

This is assuming the log is of a debug nature - if it's an audit log or something like that (something with a functional requirement) that's a different matter.

like image 44
Jon Skeet Avatar answered Oct 20 '22 11:10

Jon Skeet


I would divide the logging into three categories:

1) A requirement. Some systems require logging for audit purposes, or to fill some other requirement of the project (such as a logging standard in an app server). Then it is indeed a requirement and deserves unit tests and acceptance tests to the point where you can be confident the requirement is met. So in this case the exact string of the log may be tested for.

2) Problem solving. In case you start getting weird state in QA or production, you want to be able to trace what is going on. In general, I would say that if this is important (say in a heavily threaded application where state can get complicated but can't be reproduced via known steps) then testing that the given state values end up logged can be valuable (so you aren't testing the whole readability of the log, just that certain facts get in there). Even if the class is changed later, that state is still likely to be logged (along with additional state) so the coupling between the test and the logging is reasonable. So in this case, only parts of the logging is tested for (a contains test).

3) A development aid. In many cases I use logging as a more robust form of commenting. You can write a statement like:

 logger.debug("Extract the fifth instance of BLAH from the string " + s);

So that you can document the code and at the same time have a useful artifact if you do ever need to debug what is going on. In that case I would not unit test at all, as the existence or not of a given statement is not important on its own.

As for the view that you have to test 100% of everything, see Kent Beck's answer here. I think that the "test everything" is good advice for beginners, because when you start with TDD, the temptation will be to not test anything that is hard to test, or that pushes you to think about the design to make it testable, and rationalize it as unimportant. But once you do know what you are doing, and appreciate the value of the tests, then it is important to balance out what you are doing with what is worth testing.

like image 23
Yishai Avatar answered Oct 20 '22 10:10

Yishai