views:

273

answers:

15

In my application, I have two classes: a logger that actually logs to the database and a dummy logger that does nothing (used when logging is disabled). Here is the entire DummyLog class:

class DummyLog(object):
    def insert_master_log(self, spec_name, file_name, data_source,
                          environment_name):
        pass
    def update_master_log(self, inserts, updates, failures, total):
        pass

On one hand, I should probably let this go and not test it since there's not really any code to test. But then, my "test-infected" instinct tells me that this is an excuse and that the simplicity of the class means I should be more willing to test it. I'm just having trouble thinking of what to test.

Any ideas? Or should I just let this go and not write any tests?

+12  A: 

If you don't test, how will you really know it does nothing? :)

Sorry - couldn't resist. Seriously - I would test because some day it might do more?

n8wrl
Then shouldn't the test be written prior to implementing the code that 'does more'?
Matthew Vines
Yes but the test fixture will already be in place. I don't think there is a right or wrong answer here - I would test. You might not.
n8wrl
@Matthew Vines - Keyword there is "should". :-)
Jason Baker
@n8wrl I don't think God kills kittens when these types of tests are created. You certainly aren't doing anything wrong. Using TDD I would have probably written the case to ensure that the call to the method works, and then written the code to make it pass. Since that's all I am implementing I would have stopped there. So I can't disagree with you, but if you already have the code in place I don't think I could justify going back and creating a test case.
Matthew Vines
+1  A: 

If it doesn't do anything then there's nothing to test. If you really want you could verify that it doesn't modify any state. I'm not familiar enough with Python to know if there's an easy way to make verify that your methods don't call any other methods, but you could do that as well if you really want.

Davy8
+4  A: 

If it can't fail. There is nothing to test.

Test case results need to contain at least one successful state, and at least one unsuccessful state. If any input into the test results in successful output. Then there is not a test you could create the would ever fail.

Matthew Vines
I don't agree that that you need at least one unsuccesful state. How would you test a function performing modular integer addition? All pairs of inputs would result in a correct answer (no overflow, we wrap). You can at least test that this claimed "null" function does not crash.
djna
FYI, this is what I actually ended up doing. See my answer here: http://stackoverflow.com/questions/1127626/do-i-test-a-class-that-does-nothing/1127762#1127762.
Jason Baker
@djna -- all inputs have a correct output. But they also have incorrect output. 2+2 = 4 correct case 2+2 = 5 incorrect case. Just because a valid number came out doesn't mean it was a successful test.
Matthew Vines
@Matthew Agree, I misunderstood your definition of test case result. I thought you were arguing that some inputs had to result in errors or exceptions. So for this trivial code under consideration the test case results are it returned and it crashed. We *can* test it. [I don't actually think I *would* test it ;-]
djna
+2  A: 

Depends on your theory.

If you do a test driven type of person, then in order for that code to exist, you had to write the test for it.

If you are thinking of it as, I wrote it, how do I test it, then I think it warrants a test since you are relying on it to do nothing. You need the test to make sure someone doesn't come behind you and delete that code (it may even be you).

phillc
Heh... you caught me. :-) I've been trying to stick to TDD, but I wasn't sure how to handle this and wrote the class out before having any tests.
Jason Baker
+1  A: 

I don't know Python, but I can think of one test- check that the class actually creates without error. This will at least regression test the class and means it should work in all circumstances.

You never know someone might edit the class in the future and get it to throw an exception or something weird!

Personally though, unless you are aiming for an insanely high level of test coverage I wouldn't bother.

That said would it be catastrophic if this class did throw an exception? I'm guessing that would be one of those bugs that without a unit test would only be caught in the field.

RichardOD
+3  A: 

Be pragmatic, there is nothing to test here.

dfa
Are you saying test the nothing, or you don't need to test? :-)
RichardOD
it would be a useless test :-)
dfa
+1  A: 

Argument: You don't base your tests on the reading of the implementation but on the intended behaviour. You test that the darn thing doesn't crash when called.

This case a bit obsessive perhaps, and frankly I maybe I wouldn't bother. But it only take a small increment over these null functions for there to be pitfalls worth testing.

djna
A: 

You could test the arguments that are passed to it. If this is a dummy object that will get called with a particular set of arguments then modifying that arguments will make it fail. A test like that will ensure that if it does get modified at least no other code break that depends on it.

Andre Miller
A: 

According to the "You Ain't Gonna Need It" rule, you shouldn't write a test when there is nothing to test even if one day it might do something.

How to test that something has done nothing? That's a nice philosophical question :)

luc
A: 

I think the only useful benefit of a test for such a class is to hopefully catch if someone starts modifying it down the road. Otherwise I wouldn't bother.

whatsisname
A: 

You at least want it to not break anything when used in place of the actual logger. So reuse the actual logger tests and factor out the assertions that test that it actually logs.

Ants Aasma
+2  A: 

If anyone's interested, here's a test I ended up writing:

def test_dummy_loader():
    from loader_logs import DummyLog
    from copy import copy
    dummy = DummyLog()
    initial = copy(dummy.__dict__)
    dummy.insert_master_log('', '', '', '')
    dummy.update_master_log(0, 0, 0, 0)
    post = copy(dummy.__dict__)
    assert initial == post

Essentially, it tests that there are no attributes getting set on the object when the two dummy methods get called. Granted, it still doesn't test that the methods truly do nothing, but at least it's something.

Jason Baker
Something for nothing- sounds good.
RichardOD
+1  A: 

That DummyLogger is what is called in design pattern a "Null Object". Subclass your real Logger from that, create some test for the real logger and then use the same test but with the DummyLogger.

class TestLogger(unittest.TestCase):
   def setUp(self):
       self.logger = RealLogger()
   def test_log_debug ..
   def test_log_error ..

class TestNullLogger(TestLogger):
   def setUp(self): 
       self.logger = DummyLogger()

But As many suggested you ain't gonna need it. When it brakes, fix it.

fabrizioM
This is also referred to as the Special Case pattern- http://martinfowler.com/ap2/specialCase.html
RichardOD
+3  A: 

Of course you can test a class that doesn't do anything. You test that it does, in fact, not do anything.

In practice, that means:

  • it exists and can be instantiated;
  • it doesn't throw an exception when used; call every method and simply assert that they succeed. This also doubles as checking that the class does, in fact, define every method that it's expected to.

Don't run this test on DummyLog; use it as a generic test and run it on all loggers. When you add another method to the real Log class a year from now and forget to add it to DummyLog, the test will notice. (As long as you do remember to add a general test for the method, of course, but hopefully adding the test should be habitual, even if you forget about related classes.)

Glenn Maynard
+2  A: 

It obviously does something or you wouldn't have written it.

I'm going to guess its meant to mirror the interface of your real logging class. So test that it has the same interface, that it takes the same arguments. You're likely to change your logging while forgetting to update the dummy. If that seems redundant IT IS because they probably should just inherit from the same interface.

And then yes, you can test that it doesn't log anything. It might seem silly but its amazing what maintenance programmers will do.

Schwern