After doing code reviews and dealing with legacy code for while I felt that I should write a little about unit tests. This is kind of a selfish post since reverse engineering code just to come up with tests that are missing takes my time and will take yours too. I'll not explain unit tests or other techniques in depth since the problems I've seem are more basic.

Why write unit tests in the first place?

The answer is simple: To make changes in the code and still know that it works as expected. For a complete set of references there's enough books published about this subject. Code change is inevitable on the majority of cases, so test it, no excuses.

What should be tested?

Sometimes this is a tough discussion, but instead of writing "it depends" the simple thing is test anything that you wrote that have business rules in it. What I mean is, if you write a method that transform temperatures from Celsius to Fahrenheit test it, but avoid testing things that are out of your control. Here a simple example:

@Test
public void shouldConvertFromCelsiusToFarenheit() {
  assertEquals(37.0, new Temp().toCelsius(98.6), 0.01);
}

This test just make sure that my conversion formula is working, if some other developer need to change the code inside this method, refactor the class or else, he/she will be comfortable in doing so since there's tests for it. But often, puzzled about what to test, developers write:

@Test
public void shouldConvertFromCelsiusToFarenheit() {
  Temp temp = new Temp();
  assertNotNull(temp);
  assertEquals(37.0, temp.toCelsius(98.6), 0.01);
}

Can you spot the problem in lines 3 and 4? The problem is that the assertNotNull(temp) should hold true as long the Java specification is satisfied by the JVM. In fact this assertion is not asserting anything about the code you wrote is just making sure that once you asked for a object you got one. I know this is just two lines of code, but multiply this by thousands (perhaps millions) lines of code then you get the picture. Imagine time wasted in writing these kind of tests. So, don't do that. Also don't do assertTrue(temp instanceof Temp); which has the same problem.

What about getters and setters, should I test since someone could change the underlining implementation?

Once in the office we had this discussion and points were raised in favor and against it. You can ask yourself this question "do I have different behavior than the basic in my gets and sets?", consider the following POJO code:

public void setAge(int a) {
  this.age = a;
}
public int getAge() {
  return this.age;
}

If you test these methods, what you're testing? Again, you're testing if Java works as it supposed to. If Java doesn't behaves as it should, nothing is guaranteed anymore doesn't matter how many tests you wrote. So don't do it. But if you write in your set method:

public void setAge(int a) {
  if(a < 0)
    throw new IllegalArgumentException("age should be a positive integer");
  this.age = a;
}

Now you have two possibilities or your variable gets defined or a exception is thrown, so it's good to know that your new business rule is kept and you can write:

@Test(expected=IllegalArgumentException.class)
public void shouldThrowExceptionIfAgeIsNegative() {
  this.obj.setAge(-1);
}

Another thing, assertions and JUnit 4 exception handling use them. A common thing I see in code:

@Test
public void shouldNotBlowupWithACrazyException() throws Exception {
  new MyObject().execute();
}

In this case the developer was trying to test a "clean" execution but is not asserting anything useful, it just asserts that a exception is not thrown. This doesn't tell us much about the business rules, it just tell us "it should work". The best thing is having the all the code that execute() uses internally already covered and remove this test. If in any test an exception is thrown and not handled, JUnit will fail. Don't bother test JUnit, it is already tested.

This is just a couple of tips, there's many books and online information about unit testing, start with the basics but definitely go deep into this subject this is a valuable tool to write good code.