views:

137

answers:

4

Me and my co-workers had a bit of a disagreement last night about unit testing in our PHP/MySQL application. Half of us argued that when unit testing a function within a class, you should mock everything outside of that class and its parents. The other half of us argued that you SHOULDN'T mock anything that is a direct dependancy of the class either.

The specific example was our logging mechanism, which happened through a static Logging class, and we had a number of Logging::log() calls in various locations throughout our application. The first half of us said the Logging mechanism should be faked (mocked) because it would be tested in the Logging unit tests. The second half of us argued that we should include the original Logging class in our unit test so that if we make a change to our logging interface, we'll be able to see if it creates problems in other parts of the application due to failing to update the call interface.

So I guess the fundamental question is-- do unit tests serve to test the functionality of a single unit in a closed environment, or show the consequences of changes to a single unit in a larger environment? If it's one of these, how do you accomplish the other?

+6  A: 

You and your colleagues have stumbled upon the difference between unit tests and integration tests. Mocking everything would be done for the former; not mocking dependencies would be done for the latter.

Of course, where you draw the line for granularity is sort of subjective, too - but at the finest level of detail for unit testing, you shouldn't be worrying about anything outside the specific subject of each test.

Amber
Sometimes, when you decide something is tested and part of the *platform*, it is ok to decide to use it even inside unit tests.For example you would not fake standard library or maybe not a framework you trust to work properly. If you can afford such a trust, it might spare some lines of code.
Gabriel Ščerbák
Yes, very true - typically mocking is reserved for components that are developed as part of your project, just aren't the thing currently being tested.
Amber
A: 

The words give some hints as to the answer:

unit means 1: you are trying to test one thing. You can mock dependencies like the logging framework when that is not the primary focus of your tests. How the unit being tested interacts with it's dependencies is often part of the test case, and mocking makes this much easier.

integration means more than one thing coming together : you are trying to combine more than one thing together an test as a whole. Mocking plays a lesser role, but it can still be useful for emulating dependents that would be difficult to set up in the test scenario.

I would say that the argument for not mocking the logging system in unit tests, to see if changes break anything is quite weak. If anything is broken in the logging system, then that should be caught by a failed unit test for the logging system.

Keeping unit tests simple, clear and focused is a good rule of thumb. Yuu need this bedrock of simplicty when expanding the scope of the tests. Integration tests can quickly become complex, especially when using them as substitutes for missing unit tests - it's like "testing from a distance" and the further you move away from a component, the more difficult it is test and diagnose failures. Testing a distant dependency through a unit test is like a person trying to operate the tv-remote from the other side of the room using a fishing rod.

mdma
+3  A: 

Unit testing is like testing a single component in an electronic device.

If you want to test a single transistor in a guitar amp, you don't plug a guitar in, turn up the volume, and check the emitted sounds. That would be stupid. To check a transistor, you hook equipment up to the leads of that transistor and measure its inputs and outputs only.

At some point you do test the whole thing (guitar makes noise, etc.), but that's a different thing than testing a single transistor.

kyoryu
A: 

@David, I see that unit testing for you is doing the job its supposed to do.

You are reflecting upon the the design of your software.

From the way you're posing your question about closed environment versus open environment, it would lead me to believe that your on the cusp of having to answer the question of state-based versus behavior unit-testing for your code.

There is no hard fast rule for when to use a real object or a mock for unit testing. ONLY GUIDING PRINCIPLES! The problem of unit testing with a real Logging object is that the tests for this object will be not only in the unit tests for the logger but also in the unit tests for the objects using the logger.

So if you have 20 objects that use the logger and when you change the logger interface there will be at least 21 failed tests. This can be quite the pain when refactoring. But on the flip side if you don't use the logger class in the 20 objects unit tests and you change the logger interface you only have one failed test and 20 other unit tests that turn green, even though they will fail in production.

The only insight I can really give you is that you don't have the right abstractions in place. You might want to look up SOLID. These should be guiding principles.

Remember whenever questions come up like your asking it is the code giving you feedback. Heed the that feedback. It will save you lots of pain later.

Gutzofter