Live data from Hacker News

Unit testing best practices

howtodoinjava.com

11–17 of 17 posts

Re: Unit testing best practices

#11
post #2

"Create unit tests that target exceptions" , @Test(expected=NullPointerException.class) Don't do this, any point of your test can throw this exception. A try/catch with an assert avoids false negatives and documents where you were expecting the exception to be thrown. edit: typo

I think it's just a poor example to illustrate the point.

Firstly this is most useful when it's @Test(expected=SomeDomainSpecificException.class) NPEs can be thrown by anything.

Secondly, if you're following the other advice and have tests that test only one thing and assert only one thing then there should be only one line that can throw an Exception.

Re: Unit testing best practices

#12
> I will recommend to use Exception class and do not use specific subclasses of Exception. This will increase the test coverage also.

Firstly, it doesn't increase test coverage. You're still executing the same code paths, just weakening your assertions about the methods behavior.

Secondly, even if it did, what's the point of increasing test coverage if you're not actually saying anything useful about the contracts of your methods?

Coverage isn't the end goal that you slavishly need to satisfy, it's a useful indicator of how thorough your tests are. You don't make your tests more thorough by weakening your assertions.

Re: Unit testing best practices

#13
post #8

Earlier quoted context omitted.

I've got a missing class, for a 5 line function called from one place? I suppose I can see your point of view, but I'm not sure I agree. On your second point, I do disagree. I like to view all functions, even internals one, as having a documented API, covering things like bad inputs. This means that all functions (in debugging/assertion mode at least) promise to sanity-check their inputs for null pointers, etc. In C+…

That's not the right way to think about internal APIs. To make changes effectively you need to be willing to redefine those boundaries with a minimum of friction. If you make each function check its inputs then you won't be able to decompose your problem into small enough functions (a good function is usually Null is probably the worst mistake in programming history; never use it internally, never call an internal fu…

Just Null might be a bit limited, however there are many other similar issues (some examples I can think of include functions which take a positive integer, or a non-empty container, or a sorted container, or two containers of equal size, or an integer which is smaller than the size of another argument, which is a container).

I used to be very slack on internal checks. As times goes by I have pushed more of these into the type system where appropriate, and as checks. While unit tests on your external API can be useful, it can be hard to ensure coverage of all the nasty corner cases of your internals, some of which are only hit one time a million, or hundred million.

I find having all these checks makes me more confident about changing my internal APIs. If anyone is calling a function inappropriately for it's new interface, I will quickly get an assertion flagged up, as opposed to code carrying on calling a function incorrectly.

Just so we aren't talking at cross purposes, I feel I should say most of my programming is algorithmic, particularly in AI. In this field it can be devilishly difficult to construct good unit tests and ways of hitting bits of algorithms -- I am currently working on an algorithm which has function which I believe is required but which I cannot construct test data for my external API which causes it be called. Always a little worrying!

Re: Unit testing best practices

#14
post #8

Earlier quoted context omitted.

That's not the right way to think about internal APIs. To make changes effectively you need to be willing to redefine those boundaries with a minimum of friction. If you make each function check its inputs then you won't be able to decompose your problem into small enough functions (a good function is usually Null is probably the worst mistake in programming history; never use it internally, never call an internal fu…

Just Null might be a bit limited, however there are many other similar issues (some examples I can think of include functions which take a positive integer, or a non-empty container, or a sorted container, or two containers of equal size, or an integer which is smaller than the size of another argument, which is a container). I used to be very slack on internal checks. As times goes by I have pushed more of these int…

>As times goes by I have pushed more of these into the type system where appropriate

Yeah, the type system is the appropriate place for most of these (including null even).

>I find having all these checks makes me more confident about changing my internal APIs. If anyone is calling a function inappropriately for it's new interface, I will quickly get an assertion flagged up, as opposed to code carrying on calling a function incorrectly.

I think we may be talking at cross purposes wrt "public". I don't mean "only ever test your external API". I do mean "only ever test public methods of a class, in the technical sense of public".

Of course a large project will end up with lower-level and higher-level layers, and eventually you do need to define more rigid boundaries between the two to avoid everything becoming an unmaintainable mess, and testing at these boundaries is entirely appropriate. But a single class should belong to one layer or another.

>I am currently working on an algorithm which has function which I believe is required but which I cannot construct test data for my external API which causes it be called. Always a little worrying!

More than a little. My advice (not that you need it) would be to try and encode the constraint that leads to it not being called in the type system, then push it up through the layers.

Re: Unit testing best practices

#15

Earlier quoted context omitted.

I don't get some of your complaints. I just had a quick look through my unit tests for place where I test a private method. I have a method which looks through an array for a pair of adjacent variables, with a particular property (they have p V2 Anyway, this gets used lots of times by my algorithm. However, I added some unit tests because I wanted to make sure that the obvious corner cases (beginning and end of array…

"Anyway, this gets used lots of times by my algorithm" You've got a missing class. The fact you're so concerned with testing it highlights this. The fact it's private I should be free to rename, add or remove parameters. If I did this to your private method, I'd break unit tests, despite the code being private. For the null reference problem, it depends on your language. But the point remains the same. For public API…

When and how do you find out that your private method doesn't handle an edge case?

It seems like out of a matter of practicality that sometimes it would be best to have a unit test for a private method- dogma be damned.

Personally, if it's tricky code or something I worry about, I test it. If you change it, write your own damn test :-)

Re: Unit testing best practices

#16
> Unit testing is not about finding bugs

Of course it is!

> Mock out all external services and state

... unless those 'external services' are an essential part of you application (e.g. databases).

> Don’t unit-test configuration settings

Unit-test anything that needs to be unit-tested.

> All methods, regardless of visibility, should have appropriate unit tests

Capable of being misunderstood ...

> Aim for each unit test method to perform exactly one assertion

This limitation makes no sense.

> Do not print anything out in unit tests

Arbitrary restriciton.

> Capture results using the XML formatter

But why?

tl;dr Thanks, but I stick to my own tried and proven Unit-test guidelines.

Re: Unit testing best practices

#17
post #2

"Create unit tests that target exceptions" , @Test(expected=NullPointerException.class) Don't do this, any point of your test can throw this exception. A try/catch with an assert avoids false negatives and documents where you were expecting the exception to be thrown. edit: typo

One can precisely handle exceptions with JUnit's ExpectedException:

http://kentbeck.github.com/junit/javadoc/4.10/org/junit/rule...

This is more compact and declarative than the usual

  try {
      ...;
      fail();
  } catch (...) {
      // expected
  }
Post reply on HN