interface Service { Data get(); }
when(service.get()).thenReturn(cannedData);
interface Service { void get(Callback callback); }
doAnswer(new Answer<Void>() { public Void answer(InvocationOnMock invocation) { Callback callback = (Callback) invocation.getArguments()[0]; callback.onSuccess(cannedData); return null; } }).when(service).get(any(Callback.class));
interface Translator { String translate(String msg); }
when(translator.translate(any(String.class))).thenAnswer(reverseMsg()) ... // extracted a method to put a descriptive name private static Answer<String> reverseMsg() { return new Answer<String>() { public String answer(InvocationOnMock invocation) { return reverseString((String) invocation.getArguments()[0])); } } }
bytesToString(byte[] bytes) { ByteArrayOutputStream out = new ByteArrayOutputStream(); try { out.write(bytes); out.close() return out.toSring(); } catch (IOException e) { // This can never happen! // Should I rethrow? Eat it? Print Error? } }
Hi Misko, First I would like to thank you for the “Guide to Writing Testable Code”, which really helped me to think about better ways to organize my code and architecture. Trying to apply the guide to the code I’m working on, I came up with some difficulties. Our code is based on external frameworks and libraries. Being dependent on external frameworks makes it harder to write tests, since test setup is much more complex. It’s not just a single class we’re using, but rather a whole bunch of classes, base classes, definitions and configuration files. Can you provide some tips about using external libraries or frameworks, in a manner that will allow easy testing of the code? -- Thanks, Shahar
interface Authenticator { boolean authenticate(String username, String password); }
class LoginPage { Authenticator authenticator; boolean success; String errorMessage; LoginPage(Authenticator authenticator) { this.authenticator = authenticator; } String execute(Map<String, String> parameters, String cookie) { // do some work success = ...; errorMessage = ...; } String render(Writer writer) { if (success) return "redirect URL"; else writer.write(...); } }
class LoginServlet extends HttpServlet { Provider<LoginPage> loginPageProvider; // no arg constructor required by // Servlet Framework LoginServlet() { this(Global.injector .getProvider(LoginPage.class)); } // Dependency injected constructor used for testing LoginServlet(Provider<LoginPage> loginPageProvider) { this.loginPageProvider = loginPageProvider; } service(HttpServletRequest req, HttpServletResponse resp) { LoginPage page = loginPageProvider.get(); page.execute(req.getParameterMap(), req.getCookies()); String redirect = page.render(resp.getWriter()) if (redirect != null) resp.sendRedirect(redirect); } }
public interface PlanetaryDeathRay { public void aim(double xPosition, double yPosition); public boolean fire(); /* call this if she says the rebel base is on Dantooine */}public class BlueLaserPlanetaryDeathRay implements PlanetaryDeathRay { /* implementation here */ }public class GreenLaserPlanetaryDeathRay implements PlanetaryDeathRay { /* implementation here */ }
public abstract class PlanetaryDeathRayTestCase extends TestCase { protected PlanetaryDeathRay deathRay; @Override protected void setUp() { deathRay = createDeathRay(); } @Override protected void tearDown() { deathRay = null; } protected abstract PlanetaryDeathRay createDeathRay(); /* create the PlanetaryDeathRay to test */ public void testAim() { /* write implementation-independent tests here against deathRay.aim() */ } public void testFire() { /* write implementation-independent tests here against deathRay.fire() */ }}
public class BlueLaserPlanetaryDeathRayTest extends PlanetaryDeathRayTestCase { protected PlanetaryDeathRay createDeathRay() { return new BlueLaserPlanetaryDeathRay(); }}
public class PirateShipTest { @Test(dataProvider = "cannons") public void testFireCannonDepletesAmmunition(int ballsToLoad, int ballsToFire, int expectedRemaining) { PirateShip ship = new PirateShip("The Black Pearl"); ship.loadCannons(ballsToLoad); for (int i = 0; i < ballsToFire; i++) { ship.fireCannon(); } assertEquals(ship.getBallsRemaining(), expectedRemaining); } @DataProvider(name = "cannons") public Object[][] getShipSidesAndAmmunition() { // Each 1-D array represents a single execution of a @Test that // refers to this provider. The elements in the array represent // parameters to the test call. return new Object[] { {5, 1, 4}, {5, 5, 0}, {5, 0, 5} }; }}
<suite name="PirateShip suite" parallel="methods" thread-count="2">
@Test(expectedExceptions = { NoAmmunitionException.class })public void testFireCannonEmptyThrowsNoAmmunitionException() { PirateShip ship = new PirateShip("The Black Pearl"); ship.fireCannon();}