presentation
Upcoming SlideShare
Loading in...5
×
 

presentation

on

  • 911 views

 

Statistics

Views

Total Views
911
Views on SlideShare
911
Embed Views
0

Actions

Likes
0
Downloads
2
Comments
0

0 Embeds 0

No embeds

Accessibility

Categories

Upload Details

Uploaded via as Microsoft PowerPoint

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment
  • 38 2 1
  • 38 2 1
  • 38 2 1
  • 38 2 1
  • 38 2 1
  • 38 2 1

presentation presentation Presentation Transcript

  • Unit Testing Quickly Designing and Writing Things That Work By Jim Moore, Symantec Software, ETS
  • Which Is the Culture Where You Work?
    • “ I coded up the feature you wanted. I think it works, but I’m not sure.” “Just send it over to QA and start working on the next thing. We’ll deal with any issues they find later.” or
    • “ I coded up the feature you wanted, but I still need to add a few more tests to make sure it’s solid.” “Great, when do you think you’ll be done?”
    • The first relies on a “testing and maintenance cycle” to handle issues later. The second assumes that it’s as solid as possible before you stop working on it.
      • Bugs found while the developer is still in the mindset of that feature are much faster to fix than ones found later.
      • The developer is always better off continuing to develop from a solid foundation. (ie, It’s best not to develop against buggy code.)
  • The Problem Every programmer knows they should write tests for their code. Few do. The universal response to “Why not?” is “I’m in too much of a hurry.” This quickly becomes a vicious cycle – the more pressure you feel, the fewer tests you write. The fewer tests you write, the less productive you are and the less stable your code becomes. The less productive and accurate you are, the more pressure you feel.
    • JUnit Test Infected: Programmers Love Writing Tests
    • http:// junit.sourceforge.net/doc/testinfected/testing.htm
  • Unit Testing Is About Going Faster ...Unit Testing is about delivering working software quickly. That means you need to be able to write the code quickly, get it to work (avoid and fix defects) and to be able to change and maintain the code in future. The fastest way to deliver working software is to write correct code, straight off, in one sitting, to a known set of requirements. Most programmers can’t write code that’s perfect, and most customers change their minds about the features they want (this is a good thing – we’re embracing change). Your response as a programmer to the reality of imperfect code is to write unit tests to help check your work.
    • Mike Mason
    • http://mikemason.ca/2004/01/29#032UnitTestingIsAboutGoingFaster
  • When To Write Tests
    • Martin Fowler makes this easy for you. He says, “Whenever you are tempted to type something into a print statement or a debugger expression, write it as a test instead.”...
    • Here are a couple of the times that you will receive a reasonable return on your testing investment:
      • During Development - When you need to add new functionality to the system, write the tests first. Then you will be done developing when the test runs.
      • During Debugging - When someone discovers a defect in your code, first write a test that will succeed if the code is working. Then debug until the test succeeds.
    • JUnit Test Infected: Programmers Love Writing Tests
    • http:// junit.sourceforge.net/doc/testinfected/testing.htm
  • What Is A Test?
    • At the high-level, a test is a requirement. As every trained project manager knows, a project requirement must be “clear and testable.” Automated tests are just automatic requirement checks.
      • There are tools, like FITnesse and Mercury’s Quality Center, that handle this from the high level (functional and acceptance tests). This presentation will focus on the lower level unit tests.
    • Probably the easiest way to think of what a good unit test is, is that it’s something that verifies a piece of functionality’s “contract.”
      • Pre-conditions: Those things that must be true before you run the code. (Example: The object’s buffer has been initialized.)
      • Post-conditions: Those things that must be true after you run the code. (Example: This method will never return null.)
      • Invariants: Those things that must always be true. (Example: The list size will always be greater than or equal to 0.)
  • What Makes a Good Unit Test? Part 1
    • It sufficiently tests the contract. It doesn’t need to be complete, just “sufficient.”
      • If it’s complex code, then have lots of tests.
      • If it’s a simple accessor, then you don’t need a test.
      • Most things are in between…
    • It runs quickly. Part of the point is to make it so that you’re able to run them regularly, so speed plays into that.
      • It’s much easier to fix a bug you introduced five minutes ago than one you did five weeks ago…
    • cont…
  • What Makes a Good Unit Test? Part 2
    • Tests are independent.
      • If you have dependencies between tests, or have to do tons of setup, then it’s a pain to write tests and to run them.
      • Loosely coupled functionality enables independent tests.
      • Write loosely coupled, highly cohesive code!
    • Run the tests regularly. (Ideally, use something like CruiseControl to run the complete test suite run automatically after you check code in.)
      • One of the seriously cool benefits of having the automated tests is that it gives you automatic regression testing.
      • You can be “fearless” when you need to make changes, because you don’t have to worry that you might’ve broken something.
  • Writing Good Tests
    • If you do all the stuff that you know you’re supposed to anyway (loose coupling, high cohesion, etc.), writing tests is really easy. :-)
    • Seriously : Loosely coupled and highly cohesive software is much easier to test than tightly coupled systems, or ones where it’s hard to tell what something is really for (low cohesion).
    • There’s a lot of Patterns for making that easier, like Inversion of Control, Strategies, etc.
    • Writing the test before you write the code guarantees that your code is “testable.”
    • With very few exceptions, code that is hard to test is a HUGE flag that the design is bad (ie, tightly coupled, low cohesion).
  • Example of How To Think of Tests
    • The required functionality for a particular service (getVersionsForProductId) is the ability to get a list of all versions for a given product id. If there’s no product for that id, then an exception is thrown. If there’s no versions for a valid product id, then an empty list is returned.
  • The Tests That Are Needed
    • Starting from the easiest to the hardest:
      • If there’s no product for that id, then an exception is thrown (pre-condition)
      • If there’s no versions for a valid product id, then an empty list is returned (post-condition)
      • If there are versions for a product id, then a non-empty list of all the versions is returned (post-condition)
  • Implementing the Tests
    • def testInvalidProduct
    • begin
    • vers = getVersionsForProductId(-99)
    • assert(false, "Should have thrown an exception, but got: "+vers.to_s)
    • rescue
    • # an exception should've been thrown
    • end
    • end
    • def testNoVersionsForProduct
    • # 1234 is a legit product id, but we know it doesn't have any versions
    • vers = getVersionsForProductId(1234)
    • assert(vers.length == 0, "Got versions back: "+vers.to_s)
    • end
    • def testListOfVersionsForProduct
    • # 2345 is a legit product id, and we know it has versions
    • vers = getVersionsForProductId(2345)
    • assert(vers.length > 0, "Didn't get any versions back")
    • end
  • Tools To Help Make Testing Easy
    • The de-facto framework that everybody uses is JUnit. There’s clones in pretty much every language, like NUnit for C#. Here’s a sample:
      • using System;
      • using NUnit.Framework;
      • using System.Text.RegularExpressions;
      • namespace Notepad {
      • [TestFixture]
      • public class TestRegex : Assertion {
      • [Test]
      • public void SimplePattern() {
      • Regex r = new Regex(&quot;<p>&quot;);
      • Match m = r.Match(&quot;contains <p> here&quot;);
      • Assert(m.Success);
      • m = r.Match(&quot;contains no para&quot;);
      • Assert(!m.Success);
      • }
      • }
      • }
    • For a good list of all the various languages, go to http:// www.xprogramming.com/software.htm
  • What Is JUnit?
    • JUnit is an open source Java testing framework used to write and run repeatable tests. It is an instance of the xUnit architecture for unit testing frameworks.
    • JUnit features include:
      • Assertions for testing expected results
      • Test fixtures for sharing common test data
      • Test suites for easily organizing and running tests
      • Graphical and textual test runners
    • JUnit was originally written by Erich Gamma and Kent Beck
    - FAQ on junit.org
  • Simple Example
    • public class TestUser
    • extends junit.framework.TestCase {
    • public void testSetName() {
    • User user = new User();
    • assertEquals(“”, user.getName());
    • user.setName(“Jim”);
    • assertEquals(“Jim”, user.getName());
    • }
    • }
  • Notes On The Simple Example
    • All JUnit tests implement the Test interface, typically by extending the TestCase class.
    • The testing harness can automatically determine what to run by looking for any method that is public, returns void, and starts with “test”.
    • Test conditions are “assert”ed. On the previous screen, for example, we were asserting the equality of two values.
    • If it makes it through the test without any assertions failing, the test is considered to have passed.
  • Types of Assertions (part 1)
    • assertTrue / assertFalse
      • assertTrue(list.size() > 10);
      • assertTrue(“list.size() > 10: ”+list.size(), list.size() > 10);
    • assertEquals
      • assertEquals(10, list.size());
      • assertEquals(“Expected: 10, Actual: ”+list.size(), 10, list.size());
    • assertTrue/False is more flexible, but if it fails the message isn’t very useful unless you provide your own message.
    • assertEquals automatically shows the expected and actual values so you don’t have to write your own message.
  • Types of Assertions (part 2)
    • assertNull / assertNotNull
      • Convenience for assertTrue(obj == null)
    • assertSame / assertNotSame
      • Convenience for assertTrue(obj1 == obj2)
    • fail(msg)
      • Convenience for assertTrue(msg, false)
      • Used to indicate that a point in the code should not have been reached (eg, an exception should have been thrown)
  • Example Using “fail”
    • public void testBadLoad() {
    • try {
    • User.load(-1234); // bad id
    • fail(“Should have thrown a PersistenceException”);
    • }
    • catch (PersistenceException exp) {
    • // should have happened
    • }
    • }
  • Errors & Failures
    • At the end of testing, JUnit reports three statistics (with an implied 4 th ):
      • Tests run: the number of tests run
      • Failures: the number of assertXXX statements that failed
      • Errors: the number of uncaught Exceptions
      • Passed: (Tests run – (Failures+Errors))
  • Throwing Exceptions
    • The test methods must be public, have no return value or arguments, and begin with “test”. (There’s actually a way to get around this restriction, but it’s not worth it…) It can throw anything, so it’s common to have all the tests throw Exception so you don’t have to worry about dealing with try/catch in your test code.
      • If an exception is thrown, it’s counted as an “error” (as opposed to a “failure”)
  • Running JUnit
    • There are three basic ways to run a test:
      • Within your IDE
      • As a GUI using junit.swingui.TestRunner
      • As text using either junit.textui.TestRunner or Ant
  • Running JUnit in IDEA
  • Running JUnit As a GUI
  • Running JUnit as Text
    • java junit.textui.TestRunner test.ast.titan.business.customer.TestCustomerDao .. Time: 4.031 OK (2 tests )
    • ant test -Dtestclass=test.ast.titan.business.customer.TestCustomerDao Running test.ast.titan.business.customer.TestCustomerDao Tests run: 2, Failures: 0, Errors: 0, Time elapsed: 3.515 sec
    • Using Ant you can also easily have it do reports using the “junitreport” task…
  • JUnitReport Task Output
  • Other Options for Running Tests
    • In IDEA you can tell it to run tests for an entire package, a particular class, or a particular test method.
    • In Ant you can use the “batchtest” subtask of the “junit” task to run a specific batch of tests. (The “junitreport” task it usually used to process the results of a “batchtest” run.)
    • Both IDEA and Ant have lots and lots of options for customizing what tasks are run and how.
  • Putting Together A TestSuite
    • public static Test suite() {
    • TestSuite suite= new TestSuite();
    • suite.addTest(new TestCustomerDao(&quot;testLoad&quot;));
    • suite.addTest(new TestCustomerDao(&quot;testCommit&quot;));
    • return suite;
    • }
    • This way you can specify exactly what tests are run and in what order. If suite() is in your test class, it won’t use automatic detection of tests.
    • There’s an alternate idiom for TestSuite where you can pass it a Class instead of a TestCase, which uses automatic detection of tests in a TestCase, but allows you to string together TestCases.
      • This can be handy if you have a few test classes that should be run together (like for integration testing).
  • Why TestSuite Is Generally Evil
    • It’s duplication – you’ve already declared the test, let it do the work of finding it and running it
    • The order of your tests shouldn’t matter; if they do it’s a flashing red light with air-raid sirens that the tests are either poorly done, or that what they are testing is poorly designed
    • If you want to temporarily disable a test, it’s easiest to just prepend XX to the name (eg, “XXtestLoad”), which both causes it to not be run automatically and makes it obvious when you look at the test that it’s not being run
    • It’s recommended practice to separate the tests that you run all the time (ie, standard unit tests) and those that are too expensive to run all the time (eg, integration tests) by either package or directory structure. That way it reduces maintenance by not having to maintain TestSuites, and makes it clear which tests are for and how often they are run.
  • Running Tests Automatically
    • Whereas:
      • Running the complete test suite regularly makes sure that the minimum amount of time has past between a test breaking and discovering that fact
      • Running a complete test suite can take a “long time,” thereby being impractical to run regularly manually
      • People are forgetful, and even when it would be quick and easy to run the tests they will forget
    • Therefore be it resolved:
      • A tool such as CruiseControl ( http://cruisecontrol.sf.net/ ) should be used to automate the execution of regular builds and running of test suites
      • When a developer checks in code that breaks the test(s), notification should be sent so he/she can fix it quickly
      • If the offending developer does not immediately start fixing the problem, a wedgy should administered once an hour, upon the hour, by all the other developers who are being spammed because the tests are broken
  • Test Granularity
    • The same principles that apply to coding in general still apply to tests:
      • Each test should be highly focused, testing just one piece of functionality (high cohesion)
      • Each test should be independent of the others, without side-effects (low coupling)
      • Example: As you add tests to “testLoad” it may be best to get rid of it and split it apart into “testLoadValid”, “testLoadInvalid”, “testLoadDatabaseError”, etc.
  • Setting Up and Tearing Down
    • Before each test method, the “protected void setUp()” method is called
    • After each test method, the “protected void tearDown()” method is called
    • This is extremely useful for doing common configuration that every test in the TestCase needs (eg, opening and closing a socket connection)
    • Note: Because it’s run for each test, it’s not appropriate for expensive resource setup (eg, connecting to a database). For that use either the TestSetup decorator class, or a static variable and initialize it in the class’ static block. However, make sure that using that resource doesn’t cause side-effects between tests!
  • Other Tools For Running Tests
    • http://www.junit.org/news/extension/index.htm links to hundreds of extensions for various needs
    • Some of the ones I like:
      • NoUnit: Shows a report of what methods are actually tested (by reading your byte-code)
      • JUnitPerf: Lets you do everything from performance testing (making sure a test returns in a specific amount of time) to load testing (running a test in X threads Y times), which can also be very handy for finding intermittent problems (eg, race-conditions)
      • JFCUnit: Provides some nice tools to make it almost reasonable to test Swing code
      • HttpUnit: Provides some nice tools to make it almost reasonable to test web code
      • StrutsTestCase: Provides a mock of Struts that can be run outside a servlet engine for testing
      • Cactus: The best way to test J2EE, it’s ridiculously complicated, but that’s because testing code in J2EE containers is very complex (an alternative is to use MockEJB) – it’s best to avoid having to do this altogether by using Dependency Injection!
  • Naming Conventions
    • You can name your TestCases anything you like, but recommended practice is prepending “Test” before the name of the class you’re testing. (eg, “TestCustomerDao”)
    • Test names should reflect what it is they are testing, obviously, but what does that mean?
      • testXXX, where XXX is the method being tested
        • This is nice because it makes it obvious what method is being tested, and works well with tools
      • testXXX, where XXX is not a method name
        • This is needed when testing several methods together, or when it takes multiple tests to test a method (like the “testLoad” example being broken apart earlier)
  • Showing Test Names
    • Tools like TestDox ( http:// agiledox.sf.net / ) can be used to create “pretty” names from TestCases
      • Example:
        • public class TestFoo extends TestCase {
        • public void testIsASingleton() {}
        • public void testAReallyLongNameIsAGoodThing() {}
        • }
      • Generates:
        • Foo
        • - Is a singleton
        • - A really long name is a good thing
    • There are plugins for IDEs (like IDEA) that will do the same thing when they display tests
  • Testing Protected/Private Code
    • In general, you should only be testing public methods…
    • In Java, access privileges are a “suggestion”
    • For protected code, you can override the class in your test.
      • class A {
      • protected int m() {return 2;}
      • }
      • class B extends A {
      • @Override public int m() {return super.m();}
      • }
    • For private and protected code, you can use reflection
      • class A {
      • private int m() {return 2;}
      • }
      • class MyTestCase extends TestCase {
      • public void testM() throws Exception {
      • A a = new A();
      • Method method = a.getClass().getMethod(&quot;m&quot;, null);
      • method.setAccessible(true);
      • Integer result = (Integer)method.invoke(a, null);
      • assertEquals(2, result.intValue());
      • }
      • }
  • Common Code
    • There’s nothing that says you have to extend directly from the TestCase class. If you have a number of TestCases that requires common setup and utility methods, create an abstract class that extends from TestCase that your classes extend from.
  • What Is Test Driven Development? We start by writing some client code as though the code we want to develop already existed and had been written purely to make our life as easy as it could possibly be. This is a tremendously liberating thing to do: by writing a model client for our code, in the form of a test, we can define programmatically the most suitable API for our needs. - Dan North Java Developer’s Journal, Nov 2003
  • Some Principles of TDD
    • Main Ideas:
    • When you’re done, you’re done
    • Do as little work as possible
    • Corollaries:
    • By having tests that demonstrate that things work, you know when you’re done
    • Assume that the code you write should have APIs that exist to make your life easier
    • By having tests, you can refactor things to make them even simpler and be assured of the safety of doing so
  • Doing As Little Work As Possible Baby steps… Baby steps… - Bill Murray, “What About Bob?”
  • Sample Work List
    • Need way to load users
    • Username for user 9999 is “jmoore”
    • Firstname for user 9999 is “Jim”
    • Username for user 9998 is “smoore”
    • What if user doesn’t exist?
    • Was there a problem loading?
  • Loading Users
    • The first item is that I need a way to load users…
    • What’s the simplest way that I can ask a user to load?
      • Well, first thing that comes to mind is user.load()
    • Let’s write a test to see how well doing that would work…
  • Writing the Test
    • public void testLoad() throws Exception {
    • User user = createUser();
    • user.load();
    • }
    • Since user.load() hasn’t been written yet, this won’t even compile…
  • Compiling
    • What’s the fastest way to get the test to at least compile?
    • Add this to the User class:
    • public void load() {
    • }
    • Woo-hoo! It compiles!
  • Work List
    • Need way to load users
    • Username for user 9999 is “jmoore”
    • Firstname for user 9999 is “Jim”
    • Username for user 9998 is “smoore”
    • What if user doesn’t exist?
    • Was there a problem loading?
  • Writing the Test
    • public void testLoad() throws Exception {
    • User user = createUser();
    • user.setModelID(9999L);
    • assertTrue(!&quot;jmoore&quot;.equals(user.getUserName()));
    • user.load();
    • assertEquals(&quot;jmoore&quot;, user.getUserName());
    • }
  • Setting Username
    • The test fails.
    • What’s the fastest way to get a working test?
    • public void load() {
    • setUserName(&quot;jmoore&quot;);
    • }
    • The test passes! We’re done! Seriously. Just walk away…
  • Work List
    • Need way to load users
    • Username for user 9999 is “jmoore”
    • Firstname for user 9999 is “Jim”
    • Username for user 9998 is “smoore”
    • What if user doesn’t exist?
    • Was there a problem loading?
  • First Name
    • Let’s add assertEquals(&quot;Jim&quot;, user.getFirstName()); to the test case
    • Bummer, it failed.
    • public void load() { setUserName(&quot;jmoore&quot;); setFirstName(&quot;Jim&quot;); }
    • Yay! It passes now!
  • Work List
    • Need way to load users
    • Username for user 9999 is “jmoore”
    • Firstname for user 9999 is “Jim”
    • Username for user 9998 is “smoore”
    • What if user doesn’t exist?
    • Was there a problem loading?
  • Other User Test
    • public void testLoadSean() throws Exception {
    • User user = createUser();
    • user.setModelID(9998L);
    • assertTrue(!&quot;smoore&quot;.equals(user.getUserName()));
    • user.load();
    • assertEquals(&quot;smoore&quot;, user.getUserName());
    • }
    • When I run it, it fails.
  • Other User Load
    • public void load() { if (getModelId() == 9999L) { setUserName(&quot;jmoore&quot;); setFirstName(&quot;Jim&quot;); } else if (getModelId() == 9998L) { setUserName(&quot;smoore&quot;); setFirstName(&quot;Sean&quot;); } }
    • Yay! All tests pass now!
  • Work List
    • Need way to load users
    • Username for user 9999 is “jmoore”
    • Firstname for user 9999 is “Jim”
    • Username for user 9998 is “smoore”
    • What if user doesn’t exist?
    • Was there a problem loading?
  • Invalid User Test
    • public void testLoadInvalidUser() throws Exception {
    • User user = createUser();
    • user.setModelID(-1234L); // known bad id
    • try {
    • user.load(); fail(&quot;Should have thrown an exception&quot;);
    • }
    • catch (IllegalStateException exp) { // should have happened
    • }
    • }
    • When I run it, it fails.
  • Invalid User Load
    • public void load() throws IllegalStateException { if (getModelId() == 9999L) { setUserName(&quot;jmoore&quot;); setFirstName(&quot;Jim&quot;); } else if (getModelId() == 9998L) { setUserName(&quot;smoore&quot;); setFirstName(&quot;Sean&quot;); } else { throw new IllegalStateException( &quot;There was a problem loading user &quot;+getModelId()); } }
    • Yay! All tests pass now!
  • Work List
    • Need way to load users
    • Username for user 9999 is “jmoore”
    • Firstname for user 9999 is “Jim”
    • Username for user 9998 is “smoore”
    • What if user doesn’t exist?
    • Was there a problem loading?
  • Problem Loading
    • We’ll say that IllegalStateException should also be used if there’s a problem loading…
    • What could go wrong while loading?
    • Well, the database or network could be down…
    • Shoot, we need to make sure that the values come from the database. Better add that to the list…
  • Work List
    • Need way to load users
    • Username for user 9999 is “jmoore”
    • Firstname for user 9999 is “Jim”
    • Username for user 9998 is “smoore”
    • What if user doesn’t exist?
    • Was there a problem loading?
    • Load values from the database
  • Load from Database
    • public void load() throws IllegalStateException {
    • try {
    • // lots and lots of code for getting the
    • // values from the database...
    • }
    • catch (SQLException e) {
    • IllegalStateException exp = new IllegalStateException(
    • &quot;There was a problem loading user &quot;+getModelId());
    • exp.initCause(e);
    • throw exp;
    • }
    • finally {
    • // close all the open resources
    • }
    • }
    • Ouch, that was a lot of work between runs of tests…
    • Fortunately, all the tests we had before still pass. Yay!
  • Work List
    • Need way to load users
    • Username for user 9999 is “jmoore”
    • Firstname for user 9999 is “Jim”
    • Username for user 9998 is “smoore”
    • What if user doesn’t exist?
    • Was there a problem loading?
    • Load values from the database
  • Work List
    • Need way to load users
    • Username for user 9999 is “jmoore”
    • Firstname for user 9999 is “Jim”
    • Username for user 9998 is “smoore”
    • What if user doesn’t exist?
    • Was there a problem loading?
    • Load values from the database
    Finished!
  • Some Principles of TDD (Review)
    • Main Ideas:
    • When you’re done, you’re done
    • Do as little work as possible
    • Corollaries:
    • By having tests that demonstrate that things work, you know when you’re done
    • Assume that the code you write should have APIs that exist to make your life easier
    • By having tests, you can refactor things to make them even simpler and be assured of the safety of doing so
  • Results
    • When you’re done, you’ve got:
      • code that you know works
      • an API that is easy to use (since it was designed by actually being used)
      • highly cohesive, loosely coupled components (writing tests for anything else is too hard, and since the point is to do things the easiest way possible, this happens naturally)
      • more time, since you’re not over-designing (if it isn’t needed to make a test pass, why write it?) and not under-designing (you still have to make sure all the tests pass)
      • cont…
  • Results (cont.)
    • When you’re done, you’ve got:
      • examples on how the code is meant to be used; developers like to see code examples for an API. You also know the “documentation” is accurate!
      • documentation on what kinds of problems you were trying to solve when you wrote the code (which also nicely solves the “What the heck was I thinking when I wrote this??” problem)
      • a suite of regression tests, making maintenance much easier since you can make changes without fear
      • freedom to try things, since changes are made in small increments and there are tests to make sure nothing breaks
  • Patterns in Testing 1
    • Child Test – If a test case is getting too big, break it into smaller, more easily maintained tests
    • Mock Objects – If something is too expensive or unreliable to test with (for example, a database or other network resource), then “mocking” it out can improve speed and reliability. To guard against relying too much on the mock, make sure it can easily be switched out with the real thing.
  • Patterns In Testing 2
    • Log String – If you need to make sure a sequence is called in a particular order, append the fact that a call was made to a common string, then compare the string to what’s expected.
    • Crash Test Dummy – If you want to test that your code reacts appropriately to a condition that you can’t easily replicate (eg, database crash, network failure, etc.), artificially create the error. For example, test the screen that loads a user by testing with a version of User that throws IllegalStateException every time it’s called.
  • What About Things That Are “Hard” To Test?
    • Some things that are traditionally hard to test are:
      • GUIs
      • Databases and other “external” services
      • Things that need to run in “containers” (eg, J2EE)
    • Using a combination of Separation of Concerns and Mock Objects, it’s not hard to do this.
    • When it really is hard, it’s almost always an indication that your object is doing too much (ie, low cohesion).
  • Resources 1
    • The FAQ is excellent, and really does answer pretty much any question about JUnit itself that you’re likely to come across: http://junit.sourceforge.net/doc/faq/faq.htm
    • There are a lot of great resources (extensions, articles, use cases, etc.) referenced from the http:// junit.org site.
    • A great book about JUnit, a number of its extensions, Ant, and more is “Java Extreme Programming Cookbook” from O’Reilly.
  • Resources 2
    • http://www.junit.org/news/article/index.htm Lots and lots of articles on how to test
    • http://junit.sourceforge.net/doc/testinfected/testing.htm JUnit Test Infected: Programmers Love Writing Tests
    • Lots and lots and lots of discussions, blogs, etc.
  • & ANSWERS QUESTIONS