Friday, November 9, 2012

Testing with Mock Objects

What is a Mock Object?

A Mock Object is used in the testing of software to simulate components that are difficult to integrate with or are outside the scope of the software-under-test (more commonly a class-under-test).  Testing software is important for so many reasons, and Mock Objects make it easier to do so.

Why would someone want to use Mocks?

A commonly cited reason for not writing tests is that they are too difficult to set up.  Suppose you're working on an application that interacts with a relational database with a complicated data model.  Running a database and configuring it with seed data can be costly, time consuming, and (most importantly) tedious.  The root of the problem is that we're not trying to test the database; we're likely trying to test the business logic that operates on data in the database.  Mock objects allow your test to focus on the class-under-test, and the test becomes easier to maintain because the data it uses can be tweaked with the mocks in your test class (not in a database seed SQL script).

What are the available Mocking frameworks?

One of the most popular Mock Object frameworks for Java is EasyMock.  I've used EasyMock for a couple years with great results.  Here are some other Mock Object frameworks:


Here are some other useful resources related to Mock Objects:


How have I used Mocks?

As I mentioned earlier, I have used EasyMock for a while.  Nearly all of the Java applications I've developed in the last 7 years have used the Spring Framework.  Following the Spring Framework design tenets (Inversion-of-Control, Design-by-Contract) lead to an easily testable codebase using Mock Objects.  I've typically used Mock Objects to simulate database Data Access Objects (DAOs) SOAP Web Service & REST API clients.  Here's an example:


Example

Here's a sample of a JUnit test that uses EasyMock to mock up the access to a database.  The mock is constructed using the interface of the database DAO and is configured with the expectation that the mock method is invoked once with a certain argument.  The test will fail if the mock is not invoked or is invoked with the incorrect parameter.

JUnit Test Class

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "/testContext.xml" })
public class FooTest {

    @Autowired
    private Foo foo;

    @Test
    public void testSmoke() {
        assertNotNull(foo);
    }

    @Test
    public void testRetrieveMessage_Gold() {
        final String key = "asdf";
        final String outputMessage = "Foo!";

        // create the mock based on the interface (could also be created from a
        // concrete class!)
        final DatabaseDAO databaseDaoMock = EasyMock
                .createMock(DatabaseDAO.class);

        // indicate which methods are expected to be invoked, with what
        // parameters and return values, and the frequency of invocation
        EasyMock.expect(databaseDaoMock.retrieveMessage(key)).andReturn(
                outputMessage);

        // replay the EasyMock mock, indicating that the mock setup is finished
        EasyMock.replay(databaseDaoMock);

        // set the mock on the class-under-test, foo
        foo.setDatabaseDao(databaseDaoMock);

        assertEquals(true, foo.performBusinessLogic(key));

        // verify that the mock was called as expected
        EasyMock.verify(databaseDaoMock);
    }

    public void setFoo(Foo foo) {
        this.foo = foo;
    }

}



Class-Under-Test

public class Foo {

    private DatabaseDAO databaseDao;

    public boolean performBusinessLogic(String key) {
        databaseDao.retrieveMessage(key);

        // do something with the data...

        return true;
    }

    public void setDatabaseDao(DatabaseDAO databaseDao) {
        this.databaseDao = databaseDao;
    }

}


Mocked Interface

public interface DatabaseDAO {

    String retrieveMessage(String key);

}


Conclusion

I hope this post shows how powerful and easy Mock Objects can be.  Your code becomes much easier to test while staying easily maintainable.  Best of luck to you, regardless of which Mock Object framework you choose!

Friday, October 26, 2012

JIRA & Bamboo: A Winning Combination

It seems like the problem of managing software defects and tasks should be easy, especially in comparison to the software it's meant to track.  Easier said than done.  Each job I've worked has approached task and defect tracking differently:
  • SRA (Government contractor) used Microsoft Project in the early 2000's.  This worked ok, but only the project manager had access to the project file, which made it tedious to create/accept/resolve issues.
  • The 3D Marketing startup in 2003 used XPlanner for agile project planning.  XPlanner had a lot of the same features present in JIRA today, but it was a little rough around the edges and was not easily extensible.
  • EMC Documentum used an in-house defect tracking tool built upon the Documentum platform.  This was pretty awful.  I think the tool was built just to prove that it could be done.
  • MobiTV used the Mozilla Bugzilla bug tracking system when I joined in 2007.  It looked dated at the time, but it worked fairly well and was well supported Open Source software.  Around 2008 we switched to the Atlassian JIRA platform.  We've used that platform ever since, and I still think it's the best product available at any price.
I am definitely a fan of JIRA.  I've even gone so far as to install on a server at home for the purpose of tracking tasks for some of my personal projects.  JIRA is a Java web application that runs in a Java Servlet container (i.e. Tomcat) on multiple platforms (I'm using Linux).  The licensing and hosting options are very flexible, too.  JIRA is available as a managed service from Atlassian for a modest monthly fee.  It's also available as a download for self-hosted installations for as little as a $10 one-time fee.

JIRA embraces the idea of user stories and sizing from the Extreme Programming (XP) design methodology. It's easy to create stories that capture small amounts of work, and group those stories into development sprints that map to the release schedule.  User stories give the customer & developer a way to break the project down into manageable pieces, making it easier to schedule and prioritize features prior to construction.
Software invisibility prevents most customers from realistically conceptualizing the system in their minds.  Even if they could, communication errors would probably keep us from being able to build it from their instructions. ... A user story is the smallest amount of information (a step) necessary to allow the customer to define a path through the system.  -- "A Practical Guide to Extreme Programming" (Astels, Miller, Novak) 
Another pervasive problem is the compilation and packaging of software.  I've worked with several complex products, and it's best to automate as much of this process as possible.  Here are the approaches taken at each of my past jobs:

  • SRA used Ant to build and package Java web applications in a WAR.  The deployment platform was SunOS server, so RPMs and Debian packages were not an option.  This worked well, but the build process was triggered manually on an individual's development machine.  This was subject to the integration hell that occurs when you're not running performing continuous integration.
  • The 3D Marketing startup used Cruise Control for continuous integration (CI).  This was my first exposure to an automated build system, and it made me a true believer in the CI process.  Work out of a single codebase.  Build the final product upon every check-in.  Run unit-tests that ensure there are no regressions.  Cruise Control would package the Java Enterprise Archive (EAR) and deploy it automatically our JBoss application server, then run a suite of integration tests.
  • At EMC Documentum I created a Cruise Control build machine, but there was not any serious adoption of CI.  This was a dysfunctional organization, and code integration was the least of its worries.
  • At MobiTV we use Bamboo, also from Atlassian.  It's got a lot of polish and is tightly integrated with JIRA.  It's a great improvement over Cruise Control, and is available for a modest cost for self-hosted installations.
The integration between JIRA and Bamboo is hard to beat.  It only makes sense to tie the issue tracking system with the build system.  For instance, in a JIRA defect I can see links to all of the source control check-ins and builds associated with the issue.  Similarly, I can see the JIRA issues associated with each build in Bamboo.

Bamboo, and Continuous Integration in general, are critical to reducing the risk associated with a project.  I've got work to do, and building software is the least significant part of it.  An automated build system ensures that the build is always clean, else it alerts me to a problem so that it can be fixed when the cost is least (fix the bug as close to the time of introduction as possible).
Another important reason to accept the costs of continuous integration is that it dramatically reduces the risk of the project.  ... You never spend days chasing a bug that was created some time in the last few weeks.  And all that practice at integration comes in very handy when it comes to creating the final project.  The "production build" is no big deal.  Everyone on the team could do it in their sleep by the time it comes around, because they have been doing it every day for months. -- "Extreme Programming Explained" (Kent Beck)

In conclusion, JIRA and Bamboo are excellent tools that I hope to use on all of my future projects.  They've had a very positive influence on my development patterns and productivity, and are representative of the widespread industry acceptance of XP development methodologies over the last 10+ years.