Friday, August 31, 2012

Monday, April 9, 2012

Simple Annotation-based Unit Tests with JUnit 4

Usually when I want to unit test some code, all I need is good old

public static void main(String[] args) {
    ...
}

It's quick, it's easy, and it gets the job done. And if I had to choose 1 philosophy it would be "Pick the quickest and easiest effective way to get the job done." JUnit fits perfectly with this philosophy. It makes unit testing code even shorter than "main", and introduces useful features to boot. It can really make unit testing fun and hassle free.

Today I will go over the 4 JUnit 4 annotations that I use the most: BeforeClass, Test, Ignore, and AfterClass.

i. Including JUnit 4 in your project


Before we can do anything we need to create a project and include JUnit 4 in its build path. The easiest way to do this is to annotate a method with @Test, mouse over the error and click Import 'Test' (org.junit). Remember: quick; easy.


ii. Running the test


Right click > Run As > JUnit Test
(or Alt + Shift + X, T but c'mon)

And now we move on to the actual annotations

1. @Test


The most important one, of course, is @Test. Testing is the whole point. Methods annotated with @Test are essentially substitutes for "main". For instance, in

package baldwin.testerator;

import org.junit.Test;

public class John { 
 @Test
 public void sayHello() {
  System.out.println("Hello, old friend.");
 }
}

Your old friend John will greet you thus:
Hello, old friend.

Nice, simple, effective.

Protip! If you have multiple methods with @Test, JUnit does not guarantee any kind of execution order.

2. @BeforeClass


Now, suppose you want to do something before your tests are run. There are many times when you would want to do this. Off the top of my head I can think of: instantiating local variables like Lists, opening and initializing database connections, configuring logging, and several others.

See below:

package baldwin.testerator;

import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.Logger;
import org.junit.BeforeClass;
import org.junit.Test;

public class John {
 static Logger log = Logger.getLogger(John.class);
 
 @BeforeClass
 public static void init() {
  BasicConfigurator.configure();
  log.info("Long time no see!");
 }
 
 @Test
 public void sayHello() {
  log.info("Hello, old friend.");
 }
}

You'll notice that our friend John has acquired some bling in the form of a Log4j Logger. This allows him to say things to you in a sophisticated manner. Now he greets you like this:
0 [main] INFO baldwin.testerator.John  - Long time no see!
1 [main] INFO baldwin.testerator.John  - Hello, old friend.
How pretentious! This logging behavior is enabled by the command
BasicConfigurator.configure();
which tells Log4j to output the most basic of logging information into the Console. Any method annotated with @BeforeClass will be executed at the beginning of your test, Before any @Test methods:

package baldwin.testerator;

import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.Logger;
import org.junit.BeforeClass;
import org.junit.Test;

public class John {
 static Logger log = Logger.getLogger(John.class);
 
 @BeforeClass
 public static void init() {
  BasicConfigurator.configure();
  log.info("Long time no see!");
 }
 
 @Test
 public void sayHello() {
  log.info("Hello, old friend.");
 }
 
 @Test
 public void commentOnAppearance() {
  log.info("Wow, you have gained weight.");
 }
}

Produces:

0 [main] INFO baldwin.testerator.John  - Long time no see!
3 [main] INFO baldwin.testerator.John  - Hello, old friend.
4 [main] INFO baldwin.testerator.John  - Wow, you have gained weight.

I am starting to regret running into John after all these years.

Protip! JUnit requires @BeforeClass methods to be static. Otherwise you'll get an initializationError.

Tip: JUnit has another annotation, @Before, which will be executed once before every @Test method invocation. I don't use it nearly as much.

3. @Ignore


@Ignore does what you expect it to do. It makes JUnit ignore the annotated @Test method. In our case, it allows John to be a bit more tactful:

@Test @Ignore
 public void commentOnAppearance() {
  log.info("Wow, you have gained weight.");
 }

Produces:
0 [main] INFO baldwin.testerator.John  - Long time no see!
3 [main] INFO baldwin.testerator.John  - Hello, old friend.
Tip: @Ignore is Ignored by anything other than @Test methods. A @BeforeClass @Ignore method will still be executed, for instance.

4. @AfterClass


Finally, @AfterClass is the post-testing equivalent of @BeforeClass. You can use it to close database connections, summarize data, or anything really that you might want to do once you've completed all your tests.

We have completed our chat with John and he says goodbye:

package baldwin.testerator;

import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.Logger;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;

public class John {
 static Logger log = Logger.getLogger(John.class);
 
 @BeforeClass
 public static void init() {
  BasicConfigurator.configure();
  log.info("Long time no see!");
 }
 
 @Test
 public void sayHello() {
  log.info("Hello, old friend.");
 }
 
 @Test @Ignore
 public void commentOnAppearance() {
  log.info("Wow, you have gained weight.");
 }
 
 @AfterClass
 public static void sayGoodbye() {
  log.info("Goodbye, stay in touch!");
 }
}

0 [main] INFO baldwin.testerator.John  - Long time no see!
5 [main] INFO baldwin.testerator.John  - Hello, old friend.
7 [main] INFO baldwin.testerator.John  - Goodbye, stay in touch!

Tip: As with @BeforeClass, methods with this annotation must also be static.

Tip: As with @BeforeClass, @AfterClass has a son called @After who has ADHD and demands to be called after every @Test method. I usually ignore this annotation.

Well, wasn't that heartwarming?

Until next time, John. Until next time.

Tuesday, April 3, 2012

Shanghai Busroutes

I don't know if anybody else will ever need this, but I've made a searchable index of Shanghai's bus routes here.

It was designed to search bus stop to bus stop using the stop names. If your search term doesn't match a stop name the system will make a best guess based on something called Levenshtein Distance.  Now I'm sure Mr. Levenshtein was a very bright man but, based on my testing, relying on LD-based computerized guesses to find my way around Shanghai will result in my inner organs getting harvested so so I wouldn't even try.

As a simple usage example, if you want to to get to the People's Square (Fuzhou Road) station from the Jinke Road, Zuchongzhi Road station, first type "Jink" into the "From" box and wait for the list of relevant stops to be brought up:

Now, click on the stop name in the dropdown. This will select that stop and set the value of the text box. You will not have to remember the bus stop's name. Thankfully Shanghai bus stops are rationally named. Anyway, do the same for the "To" box and click "Search".

Voila! The power of technology will tell you that you'll have to take two rides. First the Sunqiao1Line for 3 stops, then the XinchuanLine for 21 more:


24 is a lot of stops.
Finally, if you click on the line name it will be expanded:


The relevant stops are even highlighted. Pretty cool. Worth importing a whole Javascript file to achieve that effect? You tell me. But hey, I'm doing this for the learnings.

The system itself has been up for a while but the domain baldwinternet.com is new. Click on the banner for "documentation" (it sucks). I'll try to go over some of the code in this blog in the future. Until then hey you won't ever be lost in Shanghai again. Travel safe.

Again, it's www.baldwinternet.com/busroute/

Mark

Sunday, April 1, 2012

Step 1

I know this isn't much of a post but in blogging, as in running, the first step is just to "get out there".