14

Sometimes I end up writing unit test cases for code that other developers have written. There are occasions when I really don't know what the developer is trying to do (the business part) and I just manipulate the test case to get the green line. Are these things normal in the industry?

What is the normal trend? Are the developers supposed to write unit test cases for code they wrote themselves?

1
  • 2
    "dint"? What does "dint" mean?
    – S.Lott
    Feb 25, 2011 at 19:13

6 Answers 6

12

Try reading this blog post: Writing Great Unit Tests: Best and Worst Practices.

But there are countless others on the web.

In direct answer to your questions...

  1. "Normal trend" - I guess this could differ from place to place, whats normal for me might be odd for others.
  2. I would say (in my option) the developer who writes the code should write the test, ideally using methods like TDD, where you would write the test before the code. But others may have different methods and ideas here!

And the way you described of writing the tests (in your question) is totally wrong!!

9

This approach make the unit test worthless.

You need to have the unit test fail when some real action does not work as intended. If you don't do it like that, and perhaps even write the test before the code to test, it is like having non-functioning smoke alarms.

4
  • 8
    This is not quite true. Or rather, it is true in an ideal world, but alas, often we are far from that. Consider having legacy code without tests and without specifications, and without anyone who could reliably tell you up to the minute details, what a specific piece of code is precisely supposed to do (this is reality in a large proportion of existing projects). Even in this case, it may still worth writing unit tests to lock the current state of the code, and to ensure that you don't break anything with future refactoring, bug fixes or extensions. Feb 25, 2011 at 19:08
  • 2
    Also, I guess you meant "write the test after the code to test" did you? Feb 25, 2011 at 19:14
  • @Péter, the wording went wrong - you got it right. But, if you decide to write tests they should do something to be useful. Just blindly invoking code saying it is a test, is - in my opinion - not testing.
    – user1249
    Feb 25, 2011 at 22:42
  • ørn, if you mean that we must have meaningful asserts in our unit tests, to verify that the tested code indeed does what we think it does, I fully agree. Feb 26, 2011 at 12:53
3

If you don't know what a function does then you can't write a unit test for it. For all you know it doesn't even do what it's supposed to. You need to find out what it's supposed to do first. THEN write the test.

3

In the Real World, it's perfectly normal to write unit tests for someone else's code. Sure, the original developer should have done this already, but often you receive legacy code where this just wasn't done. By the way, it doesn't matter whether that legacy code came decades ago from a galaxy far, far away, or whether one of your coworkers checked it in last week, or whether you wrote it today, legacy code is code without tests

Ask yourself: why do we write unit tests? Going Green is obviously just a means to an end, the ultimate goal is to prove or disprove assertions about the code under test.

Say you have a method that calculates the square root of a floating-point number. In Java, the interface would define it as:

public double squareRoot(double number);

It doesn't matter whether you wrote the implementation or whether someone else did, you want to assert a few properties of squareRoot:

  1. that it can return simple roots like sqrt(4.0)
  2. that it can find a real root like sqrt(2.0) to a reasonable precision
  3. that it finds that sqrt(0.0) is 0.0
  4. that it throws an IllegalArgumentException when fed a negative number, i.e. on sqrt(-1.0)

So you start writing these as individual tests:

@Test
public void canFindSimpleRoot() {
  assertEquals(2, squareRoot(4), epsilon);
}

Oops, this test already fails:

java.lang.AssertionError: Use assertEquals(expected, actual, delta) to compare floating-point numbers

You forgot about floating point arithmetic. OK, you introduce double epsilon=0.01 and go:

@Test
public void canFindSimpleRootToEpsilonPrecision() {
  assertEquals(2, squareRoot(4), epsilon);
}

and add the other tests: finally

@Test
@ExpectedException(IllegalArgumentException.class)
public void throwsExceptionOnNegativeInput() {
  assertEquals(-1, squareRoot(-1), epsilon);
}

and oops, again:

java.lang.AssertionError: expected:<-1.0> but was:<NaN>

You should have tested:

@Test
public void returnsNaNOnNegativeInput() {
  assertEquals(Double.NaN, squareRoot(-1), epsilon);
}

What have we done here? We started out with a few assumptions about how the method should behave, and found that not all were true. We then made the test suite Green, to write down proof that the method behaves according to our corrected assumptions. Now clients of this code can rely on this behavior. If someone were to exchange the actual implementation of squareRoot with something else, something that for example really threw an exception instead of returning NaN, our tests would catch this immediately.

This example is trivial, but often you inherit large pieces of code where it's unclear what it actually does. In that case, it is normal to lay a test harness around the code. Start with a few basic assumptions about how the code should behave, write unit tests for them, test. If Green, good, write more tests. If Red, well now you have a failed assertion that you can hold against a spec. Maybe there's a bug in the legacy code. Maybe the spec is unclear about this particular input. Maybe you don't have a spec. In that case, rewrite the test such that it documents the unexpected behaviour:

@Test
public void throwsNoExceptionOnNegativeInput() {
  assertNotNull(squareRoot(-1)); // Shouldn't this fail?
}

Over time, you end up with a test harness that documents how the code actually behaves, and becomes sort of a coded spec. If you ever want to change the legacy code, or replace it with something else, you have the test harness to verify that the new code behaves the same, or that the new code behaves differently in expected and controlled ways (for example that it actually fixes the bug you expect it to fix). This harness doesn't have to be complete on day one, in fact, having an incomplete harness is almost always better than having no harness at all. Having a harness means you can write your client code with more ease, you know where to expect things to break when you change something, and where they broke when they eventually did.

You should try to get out of the mindset that you have to write unit tests just because you have to, like you would fill out mandatory fields on a form. And you should not write unit tests just to make the red line green. Unit tests are not your enemies, unit tests are your friends.

1

When im writing test cases (for printers) I try to think of each little components....and what can I do to possibly break it. So lets say the scanner for instance, what commands does it use (in the pjl printer-job-language) what can I write to test every bit of functionality....Ok now what can I do to try and break that.

I try to do that for each major components, but when it comes to software and not so much hardware you want to look at each method/function and check boundaries and such.

1

It sounds like you are working with other developers (or maintaining code written by other developers) who do not do unit testing. In that case, I think you'd definitely want to know what the object or method you're testing is supposed to do, then create a test for it.

It won't be TDD because you didn't write the test first, but you could improve the situation. You might also want to create a a copy of the objects under test with stubs so that can establish that your tests function properly when the code fails.

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