This post originated from an RSS feed registered with Java Buzz
by Zohar Melamed.
Original Post: Mock around the Clock
Feed Title: corporate eejit
Feed URL: http://sedoparking.com/search/registrar.php?domain=®istrar=sedopark
Feed Description: Insanity is Normal
This came out of some testing I did with a project at work.
We have the following :
/**
* given a map of expression objects perform a lookup
* @param searchExpressions a map of SearchExpression keyed on field name
* @return
*/
public Collection lookup(Class classToSearchFor, Map searchExpressions) throws SearchException {
Session session = null;
try {
session = sessionFactory.openSession();
Criteria criteria = session.createCriteria(classToSearchFor);
Set fieldNames = searchExpressions.keySet();
for (Iterator iterator = fieldNames.iterator(); iterator.hasNext();) {
String fieldName = (String) iterator.next();
SearchExperssion sexpr = (SearchExperssion) searchExpressions.get(fieldName);
criteria.add(getHibernateExpression(sexpr));
} return criteria.list();
} catch (HibernateException e) {
// todo add the details of the map
throw new SearchException("Failed to run search for class = " +
classToSearchFor.getName(), e);
} finally {
if (session != null) {
try {
session.close();
} catch (HibernateException e) {
log.error(e.getMessage(), e);
}
}
}
}
The above function uses Hibernate's query by criteria facility to perform generic lookup for instances of any class mapped to the database using Hibernate.The hibernate SessionFactory is passed in as a parameter to the ctor of this class. (This class will be a pico component)There are 2 calls into hibernate in this function that can throw a checked exception (HibernateException in both cases).
SessionFactory.openSession
Criteria.list
We wanted to test in both cases that :
If created the session is closed when an exception is thrown ( no resource leaks )
If a Hibernate exception is thrown, the function throws a SearchException including the nested hibernate exception in the cause field
The message in the above exception is as we expect : Failed to run search for class = name of class we pass in.
Getting the latest version of MockObejcts, and using the DynaMock facility we can do the following :
The first test will cause the openSession call to fail with a hibernate exception.
As we have not created a session there is no need to test for close being called.We only verify that we get a SearchException and that it contains all we expect.
public void testHibernateExceptionTranslatedToSearchException() throws SearchException {
// create a dynamically mocked session factory
Mock mockSf = new Mock(SessionFactory.class); HibernateException hibernateException = new HibernateException("a hibernate execption");
// the code below instructs the dynamock to throw the provided exception
// when the "openSession" function is called
mockSf.expectAndThrow("openSession",hibernateException); SearchManager mySm = new SearchManager((SessionFactory) mockSf.proxy()); try {
mySm.lookup(SearchConstants.MULTI_CPTY.searachFor,
SearchConstants.MULTI_CPTY.searcahParameters);
fail("should have thrown a searchexception");
} catch (SearchException e) { assertSame("we expect our mock hibernate exception to be nested"+
"in this search exception",e.getCause(),hibernateException);
assertEquals("checking for the right message",e.getMessage(),
"Failed to run search for class = " +
SearchConstants.MULTI_CPTY.searachFor.getName());
} }
The second test will delay the failure until the list method of the Criteria is called.
By that stage we have an open session so we check both the correctness of the exception, and close being called on the open session.
Additionally we check :
openSession is called
createCriteria is called with the corrrect argument
add is called on criteria passing in only instances of the hibernate Expression class( tested using the FullConstraintMatcher )
/**
* check that the (mock) hibernate session is being closed when we throw an exception
* additionally test that if Criteria.list() throws an exception, it is dealt with in the same
* fashion as the 'other' checked exception in
* the testHibernateExceptionTranslatedToSearchException
*
* @throws SearchException
*/
public void testHibernateSessionisClosedAndGeneratedExceptionIsCorrect() throws SearchException { Mock mockSf = new Mock(SessionFactory.class);
HibernateException hibernateException = new HibernateException("a hibernate execption"); Mock mockSession = new Mock(Session.class); mockSf.expectAndReturn("openSession", mockSession.proxy()); Mock mockCriteria = new Mock(Criteria.class); mockCriteria.expectAndThrow("list", hibernateException); // the call below tells the mock criteria to expect calls to add,
// check that the argument passed in is an instance of Expression
// and return a reference to itself ( ie. "return this;" ) mockCriteria.expectAndReturn("add",
new FullConstraintMatcher(new IsInstanceOf(Expression.class)),
mockCriteria.proxy()); mockSession.expectAndReturn("createCriteria",
SearchConstants.MULTI_CPTY.searachFor, mockCriteria.proxy());
mockSession.expect("close"); SearchManager mySm = new SearchManager((SessionFactory) mockSf.proxy());
try { mySm.lookup(SearchConstants.MULTI_CPTY.searachFor,
SearchConstants.MULTI_CPTY.searcahParameters); fail("should have thrown a search exception"); } catch (SearchException e) {
assertSame("we expect our mock hibernate exception to be nested"+
"in this search exception", e.getCause(), hibernateException); assertEquals("checking for the right message", e.getMessage(),
"Failed to run search for class = " +
SearchConstants.MULTI_CPTY.searachFor.getName());
}
mockSf.verify();
mockSession.verify();
mockCriteria.verify();
}