18

Lets say I have an event listener that is called whenever a person has changed their location. Here's some Java code.

public class LocationManager() {
//..
..
..//

public void updateLocationFromGPS(String userID, String locationName) {
    if(userID!=null && locationName!=null) {
        for(LocationChangeCallback callback : locationCallbacks)
        {
            callback.onLocationChange(userID, locationName, EventSource.GPS);
        }
    }

    }
}

And the callback...

public class Callback implements LocationChangeCallback {

public void onLocationChange(String userID, String locationName, EventSource eventSource) {
//DO SOME WORK


}

}

Where the event is sent and the callback is received are in two different components, hence I will need to unit test them both. Now, knowing that before I call the callback method, I do a null check on the values, and will only send the event if the values are not null.

Therefore, the values received in the callback will never be null. Is it best practice to add tests to check for null values (and change my code to pass accordingly) in the callback? If so, why?

2
  • 2
    You asked about unit testing in the title, and asked about coding practices in the question body. Actually these are two questions. This should be reflected in the title and be clear from your question body. Or did you actually mean to ask only one question - and if yes, which one?
    – prockel
    Commented Jun 26, 2014 at 10:18
  • @prockel Just one question about unit testing, though I would need to edit my code to pass the tests. But yes, I should make it more clear, thanks!
    – Stu Whyte
    Commented Jun 30, 2014 at 8:48

5 Answers 5

27

Yes, it is. Because the unit tests are not only for the current version of the code. They are especially for all future versions and if somebody changes the code and does not know what s/he did, the test can fail and tell him that he did something wrong. So you can't say that this will never happen.

Also unit tests do not only test code, they are documentation too.

4
  • 3
    I didn't think about what can happen if someone was to change code in the future. Great, Thanks.
    – Stu Whyte
    Commented Jun 24, 2014 at 14:15
  • 3
    In practice they end up being documentation, because they happen to be working examples. However, I would hesitate to slack on documentation just because you have unit tests. The unit tests should be testing the documented requirements. The documentation is what tells you if your tests are testing the right things or not. As for the answer itself, that's spot on. Testing for things that can never happen is the easiest test to do - you clearly have a bug (caught by the test) if you've broken some invariant like "this cannot be null".
    – corsiKa
    Commented Jun 24, 2014 at 20:34
  • Sorry - I think you're mixing up some bits. A Unit Test should test an input against an expected result. If the Callback is defined as "Behaviour on Null-Input is irrelevant" then your Unit-Test to test this is "if null then assert(true)". So you don't need it at all... *see my answer
    – Falco
    Commented Jun 25, 2014 at 10:51
  • 2
    If you end up calling this method from anywhere else in your code, you would have to do the same exact check for null values prior to calling the method. I would suggest actually moving the check for null values into the method itself so that you would not need repetitive code that could easily be missed. You could then have unit tests that verified if either of the values passed to the method were null that the method would skip doing anything.
    – Sam Woods
    Commented Jun 25, 2014 at 15:39
3

There are two questions here.

One, should you unit test for unexpected inputs? Yes. Any compilable inputs are a valid unit test, and nulls, boundary conditions, and other special values should regularly be tested. It is a public method, you do not know where it may be called from in the future or what someone else may do.

Two, if your unit testing finds some unexpected result, should you code defensively to avoid the unexpected result? Sometimes. If your function could produce a meaningful result from the given input, then you should modify it to do so. If your function could not produce a meaningful result (for example, if you are asked to open a file for reading when the file does not exist), then you should update your unit test to expect the error or exception. In either case you should end with a passing unit test.

Edit: Also, personally, one of the great benefits of unit testing is that you can produce edge and special cases on demand, even if they are difficult to find or appear impossible in the real world. There are billions of anecdotes about systems which were functioning properly for years until suddenly they weren't because someone changed some value somewhere off in a far corner of the database.

1

If it is part of the "defined interface" for onLocationChange that it must not be invoked with nulls, then there's not much point testing for its behaviour on nulls. You don't care what its behaviour on nulls is; if it is ever invoked with nulls, then the bug is in the caller.

From that, we can also conclude that you should absolutely be unit-testing all callers (including updateLocationFromGPS) such that you make sure they never pass nulls to onLocationChange. So declaring "nulls are just invalid, it's the caller's fault if I get them" generally increases your testing load, rather than decreasing it.

On the other hand, if there is some well-defined behaviour for onLocationChange when given nulls, then you should unit test its behaviour on nulls. Even if you never pass it nulls in your code base at the moment, and even if the well-defined behaviour is just throwing some particular exception.

0

After reading the answers I feel this is an important distinction to point out. 'Unit testing' and 'Checking parameters in your code' are two distinct things.

This is checking the parameters. My method checks there aren't null values before it does something invalid.

It documents assumptions you made in the method, enforces them, and fails fast. It's thing that's good to do with far more than null values. Also if they're distinct enough to be separately tested, their distinct enough to be reused separately.

So the answer to the body of the question (should you check parameters) is yes

public void Foo(string myString){ 
    Guard.IsNotNull(myString);
    return foo.ToUpper();
}

This is unit testing

public static void Foo(string myString){ 
    return foo.ToUpper();
}

[TestMethod]
[ExpectedException(typeof(NullReferenceException),"It went bang")]
public void TestWithNulls()
{
   SomeClass.Foo(null);
}

That wasn't terribly useful. Don't do that here. Unless you feel it's a key part of the functionality that the method goes bang.

-1

Ok - so what is a Unit-Test designed for? Essentialy to test/verify that your method behaves like it should be according to the documentation/requirements.

So if a method is documented as "always returns 1" you would design unit tests to verify that whatever the inputs are, the function always returns 1.

Your Callback seems to be defined as "Does something XYZ. If Input is NULL will behave unexpected" If that is your requirement and the documentation of your Method, you don't have to write a Unit-Test for the inputs being NULL, because whatever your method does will be correct behavior according to the definition!

Because what would you test in your Unit test? Input = NULL -> Expected Result = ??? what would you assert, if there is no defined behavior in the specifications ?

2
  • 1
    He could throw a Exception in the method under test and assert that a Exception will be thrown.
    – Twaldigas
    Commented Jun 25, 2014 at 11:31
  • Or if the method under test change something in a database or similar, the unit test could check that the database is unchanged.
    – Twaldigas
    Commented Jun 25, 2014 at 11:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.