Deprecated: Assigning the return value of new by reference is deprecated in /home/therning/public_html/niklas/wp-settings.php on line 472

Deprecated: Assigning the return value of new by reference is deprecated in /home/therning/public_html/niklas/wp-settings.php on line 487

Deprecated: Assigning the return value of new by reference is deprecated in /home/therning/public_html/niklas/wp-settings.php on line 494

Deprecated: Assigning the return value of new by reference is deprecated in /home/therning/public_html/niklas/wp-settings.php on line 530

Deprecated: Assigning the return value of new by reference is deprecated in /home/therning/public_html/niklas/wp-includes/cache.php on line 103

Deprecated: Assigning the return value of new by reference is deprecated in /home/therning/public_html/niklas/wp-includes/query.php on line 21

Deprecated: Assigning the return value of new by reference is deprecated in /home/therning/public_html/niklas/wp-includes/theme.php on line 623
Niklas blog » Blog Archive » jMock-solid Testing

Niklas blog

Just another WordPress weblog

jMock-solid Testing

May 11th, 2005 in Uncategorized

Deprecated: Function split() is deprecated in /home/therning/public_html/niklas/wp-content/plugins/google-analytics-for-wordpress/googleanalytics.php on line 466

Deprecated: Function split() is deprecated in /home/therning/public_html/niklas/wp-content/plugins/google-analytics-for-wordpress/googleanalytics.php on line 466

Deprecated: Function split() is deprecated in /home/therning/public_html/niklas/wp-content/plugins/google-analytics-for-wordpress/googleanalytics.php on line 466

Deprecated: Function split() is deprecated in /home/therning/public_html/niklas/wp-content/plugins/google-analytics-for-wordpress/googleanalytics.php on line 466

Deprecated: Function split() is deprecated in /home/therning/public_html/niklas/wp-content/plugins/google-analytics-for-wordpress/googleanalytics.php on line 466

Deprecated: Function split() is deprecated in /home/therning/public_html/niklas/wp-content/plugins/google-analytics-for-wordpress/googleanalytics.php on line 466

Deprecated: Function split() is deprecated in /home/therning/public_html/niklas/wp-content/plugins/google-analytics-for-wordpress/googleanalytics.php on line 466

Some time ago I started using jMock to create mock objects in my unit tests at work. If you haven’t heard of mock objects before it’s really quite simple — mock objects are used during unit testing as stand-ins for those objects the class you’re testing interacts with. So when you unit test a class in your application’s service layer which depends on a DAO, instead of using the real DAO you would use at runtime you use a mock object which implements the DAO interface. To the class under test this mock object will look as if it was a real DAO implementation.

The really powerful thing with mock objects is that they allow you to check if the class under test uses the mock objects as expected.

Basically you have four options when you need to mock objects:

  1. Implement them yourself just as you would create any other class.
  2. Use premade mock objects. Obviously you won’t be able to find any premade mock objects for your custom interfaces. But if you need to mock standard interfaces such as the HttpServletRequest interface there are freely available libraries. The Spring Framework, which I have used a lot, includes mock object implementations for some common standard interfaces. Over at mockobjects.com you will also find useful mock object implementations.
  3. Use a static mock object creator such as MockCreator. This will create a skeleton class which implements your interface.
  4. Use a dynamic mock object creator such as jMock or EasyMock. These will, at test time, create objects which implement the required interfaces.

Options 3 and 4 are the most flexible when you’re mocking custom interfaces. Typically both static and dynamic mock object creators let you specify expectations on the mock objects. Expectations define what operations we expect the class under test will perform on the mock object.

Let’s have a look at a simple example using jMock. Let’s pretend we’re developing software for a bank. We will need an Account interface encapsulating the details of an account and an AccountManager interface which performs operations on accounts. In this example the only operation in the AccountManager interface will be the transfer() operation which transfers a specified amount of money from one account to another. Here are these interfaces:

public interface AccountManager {
	void transfer(Account from, Account to, double amount)
		throws OutOfMoneyException;
}

public interface Account {
	void charge(double amount) throws OutOfMoneyException;
	void deposit(double amount);
}

The bank in this example is a particularly greedy bank — whenever money is transferred from one account to another a service charge of 10 SEK (which is about €1 or $1.3) plus 10% of the amount is moved from the source account to the bank’s special bonus account. At the end of the year the bonus account is emptied and all the money is spent on sex and drugs by the bank’s managers.

Now we want to implement this behavior. We will be doing this test-first, i.e. we will write the test for the behavior before we write the actual AccountManager implementation. Let’s pretend we have an AccountManagerImpl which needs a reference to the special bonus account as its only constructor argument. In this test we will transfer 100 SEK from account1 to account2. This is what the test could look like when using jMock:

public class AccountManagerImplTest extends MockObjectTestCase {
	public void testTransferWithoutOvercharging()
		throws Exception {

		Mock mockAccount1 = mock(Account.class);
 		mockAccount1
			.expects(once())
			.method("charge")
			.with(eq(100.0 * 1.10 + 10.0));
		Mock mockAccount2 = mock(Account.class);
		mockAccount2
			.expects(once())
			.method("deposit")
			.with(eq(100.0))
			.after(mockAccount1, "charge");
		Mock mockBonusAccount = mock(Account.class);
		mockBonusAccount
			.expects(once())
			.method("deposit")
			.with(eq(100.0 * 0.10 + 10.0))
			.after(mockAccount1, "charge");

		Account account1 = (Account) mockAccount1.proxy();
		Account account2 = (Account) mockAccount2.proxy();
		Account bonusAccount =
			(Account) mockBonusAccount.proxy();

		AccountManagerImpl man =
			new AccountManagerImpl(bonusAccount);
		man.transfer(account1, account2, 100.0);
	}
}

The first lines of code

	Mock mockAccount1 = mock(Account.class);
	mockAccount1
		.expects(once())
		.method("charge")
		.with(eq(100.0 * 1.10 + 10.0));

creates a new Mock object using jMock. Notice how our test class extends jMock’s MockObjectTestCase. This mock object will implement the Account interface and will be used later as the source account.

We then add one expectation to the mock object. We expect our AccountManagerImpl to call the account’s charge() method once with a single argument which should be equal to 100 * 1.10 + 10 = 120.

In the next lines of code

	Mock mockAccount2 = mock(Account.class);
	mockAccount2
		.expects(once())
		.method("deposit")
		.with(eq(100.0))
		.after(mockAccount1, "charge");

we create the mock object for the destination account. This time we expect that the account’s deposit method will be called once with an argument equal to 100.0. Furthermore we expect this method to be called after the charge method of the previously created mock account. By adding this after-expectation we ensure that the AccountManagerImpl class will never deposit money in the destination account before the money has been charged successfully (no OutOfMoneyException is thrown) from the source account.

Next we create the mock object for the bank’s bonus account:

	Mock mockBonusAccount = mock(Account.class);
	mockBonusAccount
		.expects(once())
		.method("deposit")
		.with(eq(100.0 * 0.10 + 10.0))
		.after(mockAccount1, "charge");

Once again we expect the deposit method to be called once. This time with an argument equal to 100.0 * 0.10 + 10.0 = 20. Once again the call should be made after the source account has been charged to prevent any imbalance if the source account throws an OutOfMoneyException.

Now all we have to do is create the accounts from the mock objects:

	Account account1 = (Account) mockAccount1.proxy();
	Account account2 = (Account) mockAccount2.proxy();
	Account bonusAccount = (Account) mockBonusAccount.proxy();

And finally create the AccountManagerImpl and call its transfer method:

	AccountManagerImpl man =
		new AccountManagerImpl(bonusAccount);
	man.transfer(account1, account2, 100.0);

Notice how we never call any of JUnit’s assert* methods. When the test method ends the super class of our test case, MockObjectTestCase, will check that all expectations on the mock objects we’ve created have been met. If they haven’t an AssertionFailedError will be thrown and the test will fail. jMock will also make the test fail if a method is called which isn’t expected to be called or if the arguments in a call to an expected method don’t match the argument expectations we defined.

The next test method will demonstrate how we can use jMock to simulate what happens when an exception is thrown. Here’s the new test method:

public class AccountManagerImplTest extends MockObjectTestCase {
	public void testTransferWithoutOvercharging()
		throws Exception {
		...
	}

	public void testTransferWithOvercharging()
		throws Exception {

		Mock mockAccount1 = mock(Account.class);
		mockAccount1
			.expects(once())
			.method("charge")
			.withAnyArguments()
			.will(throwException(new OutOfMoneyException()));
		Mock mockAccount2 = mock(Account.class);
		Mock mockBonusAccount = mock(Account.class);

		Account account1 = (Account) mockAccount1.proxy();
		Account account2 = (Account) mockAccount2.proxy();
		Account bonusAccount = (Account) mockBonusAccount.proxy();

		AccountManagerImpl man =
			new AccountManagerImpl(bonusAccount);

		try {
			man.transfer(account1, account2, 100.0);
			fail("OutOfMoneyException not thrown");
		} catch (OutOfMoneyException oome) {
		}
	}
}

This time we have changed the expectations on the source account slightly:

	Mock mockAccount1 = mock(Account.class);
	mockAccount1
		.expects(once())
		.method("charge")
		.withAnyArguments()
		.will(throwException(new OutOfMoneyException()));

Once again we expect the charge method to be called once on the source account. This time, however, no matter what the arguments are, an OutOfMoneyException will be thrown. Furthermore we have removed the expectations on the other two accounts.

When the AccountManagerImpl in this test calls account1.charge() an OutOfMoneyException will be thrown. Since jMock will fail the test if an unexpected call is made to any of the mock objects we can be certain that, if the test succeeds, no money has been charged or deposited from/to any of the accounts.

I realize that this is a very simple example. It only shows off some of the most basic functionality jMock offers. To learn more about the features of jMock have a look at the tutorials over at the jMock site: http://jmock.codehaus.org/docs.html#tutorials.

// Niklas

8 Responses to “jMock-solid Testing”

  • emacc
    March 3rd, 2007 at 20:18

    Thanks for posting this explanation! I’ve been trying to “get” the explanations on the jMock site, but I just couldn’t “get” “it”. Your explanation has helped tremendously! ;-)

  • niklas
    April 2nd, 2007 at 10:35

    I’m glad I could help!

    // Niklas

  • Jacob Thomas
    April 3rd, 2007 at 01:10

    Some people are gifted with explaining things in a very simple manner. This article is the best and fast introduction to JMock I have come across. Brilliant!!!!!!

  • Anonymous
    April 25th, 2007 at 15:31

    A very Good sample demonstration for beginers man great article

  • Jaikrat
    May 6th, 2007 at 11:43

    hi, This is helpfull, but I was lukin JMock for those application who has session(context) kind of thing in nature. pls if nybody hav any example of this problem pls let me knw.

    Thanks

  • Iyengar
    November 3rd, 2007 at 00:55

    Please write another article for the latest version of Jmock 2.4…

  • Calvin Leung
    October 12th, 2008 at 10:55

    Thx. very useful for new comer :)

  • vas
    March 22nd, 2010 at 20:35

    Really helpfull. I really don’t understand when i read the docs in Jmock.