Unit testing tips and tricks

There are some common patterns when it comes to unit testing. Not very hard to solve situations, however, it might take a little time to find a solution to them, especially for the first time.

In this post I’d like to share some ideas that always pop up when someone new to unit testing joins our team. These things may seem trivial after some practicing, but still. Someone might find it useful. The code examples will be in Java, the mocking -when applicable- will be done using JMock (the same principles apply to Mockito).

  • Dummies (Implementations, not people)

Let’s say you are working on a large legacy system, where you face something like this:

public class Authenticator {
	//...
	public boolean canProceed() {
		if (isAuthorized()) {
			auditLogger.fine("Access granted");
			//... other stuff here ...
			return true;
		}
		return false;
	}

	protected boolean isAuthorized() {
		//...
		//if (!isConnectionAvailable) {
			throw new UnableToConnectException();
		//}
		//...
	}
}

So, we need a test case to check that the method returns true when authorization is ok. We start with something like:

	private Authenticator authenticator = new Authenticator();

	@Test
	public void shouldReturnTrueWhenAccessIsGranted() {
		assertTrue(authenticator.canProceed());
	}

The problem here is that the isAuthorized() method tries to talk over the network and keeps throwing exceptions. No problem, let’s overcome the problem by using a dummy implementation of that method. A dummy implementation is nothing more than the behavior we need for our test case:

	private Authenticator authenticator = new Authenticator(){
		@Override
		protected boolean isAuthorized() {
			return true;
		}
	};

Now the test will pass, as it ignores the concrete implementation of the problematic method. Note: the method isAuthorized() could have been private. Don’t panic, just make it protected. I know it’s a broader visibility modifier, but for tests’ sake it’s perfectly acceptable.

  • Mocking static calls

This pattern has two variants: when the static call is not important from the application logic’s point of view and when it actually is.

1.) The invocation is not important

Sometimes it might happen that the call is not closely related to business logic, therefore you decide that you don’t want to test the interactions the static calls represent. Let’s consider the following code snippet:

public class Authenticator {
	//...
	public void proceed() {
		auditLogger.fine("proceeding");
		UnimportantClass.unimportantMethod();
		authorizationListener.doNotifyAll();
	}
}

Now again, we try to test the proceed() method, and write something like:

	private Logger logger = context.mock(Logger.class);
	private AuthorizationListener authorizationListener = context.mock(AuthorizationListener.class);

	@Test
	public void shouldTestProceed() {
		context.checking(new Expectations() {
			{
				ignoring(logger);
				oneOf(authorizationListener).doNotifyAll();
			}
		});
		authenticator.proceed();
	}

So far so good, we’ve got all the interactions, but what about the static call, that throws exceptions. Well, we’d need some dummy behavior for that (since it is not important)… And -BAAANG!- we’re back to dummies. We just simply extract the static call into a wrapper method, and override it in the test class.

public class Authenticator {
	//...
	public void proceed() {
		auditLogger.fine("proceeding");
		unimportantCall();
		authorizationListener.doNotifyAll();
	}

	protected void unimportantCall() {
		UnimportantClass.unimportantMethod();
	}
}

and in the test, we change the declaration of Authenticator:

	private Authenticator authenticator = new Authenticator() {
		@Override
		protected void unimportantCall() {
		}
	};

And that’s it. It’s running now. We happy, Jules.

2.) The invocation is important

In this case we actually want to see that the actual invocation is made, so a dummy implementation does not help. This case also starts as the previous one, because we, again, extract the static call into a method, but this time we further extract it into its own class. Of course we have to inject the newly created class, so we can mock it (use your favorite injection technique). So we end up with something like this:

public class Wrapper {
	public void importantCall() {
		ImportantClass.importantMethod();
	}
}

public class Authenticator {
        //...
	private Wrapper wrapper;

	public Authenticator(Wrapper wrapper) {
		this.wrapper = wrapper;
	}

	public void proceed() {
		auditLogger.fine("proceeding");
		wrapper.importantCall();
		authorizationListener.doNotify();
	}
}

And in the test class:

	private Logger logger = context.mock(Logger.class);
	private AuthorizationListener authorizationListener = context.mock(AuthorizationListener.class);
	private Wrapper wrapper = context.mock(Wrapper.class);
	private Authenticator authenticator = new Authenticator(wrapper);

	@Test
	public void shouldTestProceed() {
		context.checking(new Expectations() {
			{
				ignoring(logger);
				oneOf(wrapper).importantCall();
				oneOf(authorizationListener).doNotify();
			}
		});
		authenticator.proceed();
	}

Such a way we overcame the static invocation problem and have our checked invocation.

  • Mocking final classes

Final classes are pain (read Michael Feathers’ thoughts on final classes here).  The problem is that JMock cannot mock final classes as it cannot subclass your class. In those rare cases you face the problem of mocking such classes you can do two things:

1.) Extract a delegate

Let’s say we have:

public final class Wrapper {
	public void importantCall() {
		ImportantClass.importantMethod();
	}
}

Now if you want to run the tests we created above, JMock will throw an IllegalArgumentException telling you that it can’t subclass Wrapper.

In this case you can create a new class, with a reference to the final class. Now, you delegate every single call to the final class, and mock the delegate. Let’s see that in practice:

public class WrapperDelegate {

	private final Wrapper wrapper;

	public WrapperDelegate(Wrapper wrapper) {
		this.wrapper = wrapper;
	}

	public void importantCall() {
		wrapper.importantCall();
	}
}

public class Authenticator {
//...

	private WrapperDelegate delegate;

	public Authenticator(WrapperDelegate delegate) {
		this.delegate = delegate;
	}

	public void proceed() {
		auditLogger.fine("proceeding");
		delegate.importantCall();
		authorizationListener.doNotify();
	}
}

Note that class Authenticator now uses the delegate object; in the test class we have to mock WrapperDelegate instead of Wrapper, like this:

	private Logger logger = context.mock(Logger.class);
	private AuthorizationListener authorizationListener = context.mock(AuthorizationListener.class);
	private WrapperDelegate delegate = context.mock(WrapperDelegate.class);
	private Authenticator authenticator = new Authenticator(delegate);

	@Test
	public void shouldTestProceed() {
		context.checking(new Expectations() {
			{
				ignoring(logger);
				oneOf(delegate).importantCall();
				oneOf(authorizationListener).doNotify();
			}
		});
		authenticator.proceed();
	}

2.) Extract an interface

In this case we extract an interface out of the final class. That interface will declare all the methods the final class has. Now we change the declaration in our class-under-test, so it references the newly extracted interface instead of the final class. Let’s see how it looks like:

public interface MockableInterface {
	void importantCall();
}

public final class Wrapper implements MockableInterface{
	public void importantCall() {
		ImportantClass.importantMethod();
	}
}

public class Authenticator {
//...
	private MockableInterface wrapper;

	public Authenticator(MockableInterface wrapper) {
		this.wrapper = wrapper;
	}

	public void proceed() {
		auditLogger.fine("proceeding");
		wrapper.importantCall();
		authorizationListener.doNotify();
	}
}

And so in the test we now mock and inject the interface instead of the final class:

private MockableInterface wrapper = context.mock(MockableInterface.class);

//...

private Authenticator authenticator = new Authenticator(wrapper);
  • Done – yay!

I guess that’s it. Just don’t be afraid to change things’ visibility so you can test them. Also don’t be afraid to create classes and/or interfaces just to be able to create test cases.

Hope it helps.

Advertisements

Author: tamasgyorfi

Senior software engineer, certified enterprise architect and certified Scrum master. Feel free to connect on Twitter: @tamasgyorfi

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s