Simplifying Logging in Test Code
Why Logging in Test Code is Bad
Logging is a production code feature, and although it may make sense at first to use it in test code, it really doesn't. Logging is just an additional line of code that doesn't bring any value in test code. It certainly does in production code, but test code is much different. Test code is an isolated piece of code or functionality that you are testing. It doesn't run the same as your production code. In test code, you setup data, perform an operation to test, then assert the result is what is expected.
If a test fails, it will fail from either an exception being thrown or an assertion that failed. When this happens, something has changed. You either need to update the test code because of a change in functionality or there has been a bug introduced in production code.
The test data is always going to be static at some point. You should be able to perform the same operation with the same data and get the same result. Think of it the same way as a pure function. Your test code is your log. It is 100% reliable and more valuable than any log could provide.
final var x = 1;
final var y = 2;
assertThat(x + y, is(3));
This has two variables of data. The variables could come from anywhere in a more complex test, but in this test, they are just hard-coded values. Since the data is static, you know the data you are working with. Even though they are variables, you can think of them as literal values.
assertThat(1 + 2, is(3));
This should always equal three because the data isn't changing.
In the previous example, what would you log to it to give it any value? The data is static in test code. Even if you are dynamically generating data and the assertion fails, it will include the value that was expected and the actual value. This allows you to plug in the failed value and debug the code.
Can't I Control Logging With The Log Level
Log levels make sense in production code. In test code, if you are logging, you are either going to want the log or not want the log. A log level doesn't make sense in test code. Why would you need to turn something on and off when it doesn't add value in the first place?
Debugging Failing Tests
If a test fails, you are going to use the debugger instead of looking at logs. In more complex tests, such as integration tests, you may need to resort to looking at logs. If you run into a case where you need the logs, use your production code logs; don't add logging to the test code. If you can't use production code logs for tests, how useful will they be for debugging production? If an instance of your application has a problem for a customer and you have to figure out what is wrong with that instance, you are going to want to see the logs.
If you need the logs, they should be what your production code already logs.
Is Logging in Test Code Ever Okay?
If you have a test code that is multi-threaded, there are use cases where you are going to want to use logging in test code. The data doesn't change, so the outcome shouldn't change. When you are working with multi-threaded code, this is still true, but the execution of that order can't be controlled. Because of this, you may need to log something to determine how the different threads executed, what their state is in, or the order they were executed. Logging can be very useful for this. In this case, logging in test code should be used. Don't use it just because it is multi-threaded code. Use it only when you will benefit from it.
Conclusion
Simplifying logging in test code is really easy; don't use logging in test code. If you feel like you need a log, something could probably be improved in the test code. The exception to this rule is when working with multiple threads in test code.