I found that there is nice extension to JUnit called Unitils (http://www.unitils.org). It offers some generic utilities for unit testing, EasyMock mock object management, database testing (either plain JDBC or Hibernate). Also it's integrated with DBUnit and Spring.
General testing utilities
This feature set contains some things that are sometimes quite useful like reflection based equality (which we have more or less implemented ourselves in many projects I believe), ignoring order of collection elements etc. There are also some features that seem somewhat less useful like ignoring default values or asserting fields using the property path (e.g "address.street") instead of getters.
Database testing utilities
The most interesting thing here I think is the ability to test Hibernate mappings using just following statement: HibernateUnitils.assertMappingWithDatabaseConsistent(). If there are some mismatches it will generate the DDL statements that can be used to update your database schema. So to test all your mappings you only need following test case:
@SpringApplicationContext({"testContext.xml"})
public class DatabaseMappingsTest extends UnitilsJUnit4 {
@Test
public void objectsAreMappedCorrectly() {
HibernateUnitils.assertMappingWithDatabaseConsistent();
}
}
and at least following Spring beans XML in your classpath if :
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="annotatedClasses"> <list> <value>hibernate.domain.User</value> <value>hibernate.domain.Permission</value> ... </list> </property> </bean> <bean id="dataSource" class="org.unitils.database.UnitilsDataSourceFactoryBean" />
As I mentioned already Unitils integrates very nicely with Spring so in order to load Spring application context all you need is the @SpringApplicationContext annotation.
Here is a sample for testing Hibernate HQL:
@Transactional(TransactionMode.ROLLBACK)
@SpringApplicationContext({"testContext.xml"})
public class UserDaoImplTest extends UnitilsJUnit4 {
private UserDao userDao;
@SpringBeanByType
private HibernateTemplate hibernateTemplate;
@Before
public void initDao() {
UserDaoImpl dao = new UserDaoImpl();
dao.setHibernateTemplate(hibernateTemplate);
userDao = dao;
}
@Test
public void save() throws Exception {
User user = createNewTestUser();
userDao.save(user);
User retrieved = userDao.getUser(user.getId());
assertEquals(user, retrieved);
}
}
In order to get this sample working following beans need to be added to the testContext.xml
<bean id="hibernateTemplate" class="hibernate.dao.SessionFlushingHibernateTemplate"> <property name="sessionFactory"> <ref bean="sessionFactory" /> </property> </bean> <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory"> <ref local="sessionFactory" /> </property> </bean>
| Sidenote Save test above is reliable only because of custom HibernateTemplate. Probably everybody already knows this that in order to test Hibernate we need to flush/clear session after every updating method because otherwise Hibernate will not send changes to database and will return exact same instance of entity the next time we try to get it from ongoing session. In AMS there is class called HibernateTemplateProxy which is doing this but it has special registry of deleted entities. Does anyone know why exactly it needs to be there? Especially as the overridden load method is only one way of materializing something from persistence. |
Mock object utilities
Basically Unitils removes the need to manually create mocks and simplifies replaying and verifying. Features like injecting mocks seem a bit fishy to me but maybe I'm just too old fashioned.
Summary
In many aspects Unitils offers similar functionality that is available in Spring 2.5 test packages and probably there are some alternative unit testing libraries also. However if you can't upgrade to Spring 2.5 or want to simplify testing Hibernate/DB access code I think Unitils is quite handy.
Juhuu.. so there is an electronic version IT year book for 2007:
http://www.riso.ee/et/node/355 http://www.riso.ee/et/pub/2007it/
I have been long time wondering if it is possible to improve API of EasyMock Java 1.3 version so that you get rid of MockControls in your test code. So as an experiment I wrote following classes to check how much work does this require. I wanted to achieve same simple API as in EasyMock 2.x but still be Java 1.4 compatible. Also I didn't want to rewrite EasyMock or extend it in any fragile way.
Here are the resulting classes that currently support only returning booleans but with some simple coding it should be possible to make it support whole API of EasyMock:
public class EasyMockHelper { private static MockControl lastInvokedControl; public static ReturnValueSetter expect(boolean b) { return new ReturnValueSetter(lastInvokedControl); } public static Object getMock(Class cl) { Object proxy = Proxy.newProxyInstance(cl.getClassLoader(), new Class[]{cl}, new MockInvocationHandler(MockControl.createControl(cl))); return proxy; } private static class MockInvocationHandler implements InvocationHandler { private final MockControl control; public MockInvocationHandler(MockControl control) { this.control = control; } public MockControl getControl() { return control; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object retValue; try { retValue = method.invoke(control.getMock(), args); } finally { lastInvokedControl = control; } return retValue; } } public static void replay(Object mock) { getControl(mock).replay(); } private static MockControl getControl(Object mock) { MockInvocationHandler invocationHandler = (MockInvocationHandler) Proxy .getInvocationHandler(mock); return invocationHandler.getControl(); } public static void verify(Object mock) { getControl(mock).verify(); } }
public class ReturnValueSetter { private final MockControl lastInvokedControl; public ReturnValueSetter(MockControl lastInvokedControl) { this.lastInvokedControl = lastInvokedControl; } public void andReturn(boolean b) { lastInvokedControl.setReturnValue(b); } }
And here is basic test:
public class EasyMockHelperTest extends TestCase { private interface Sample { boolean getBoolean(); } public void testExpectReturnTrue() throws Exception { Sample mock = (Sample) EasyMockHelper.getMock(Sample.class); EasyMockHelper.expect(mock.getBoolean()).andReturn(true); EasyMockHelper.replay(mock); assertTrue(mock.getBoolean()); } public void testExpectReturnFalse() throws Exception { Sample mock = (Sample) EasyMockHelper.getMock(Sample.class); EasyMockHelper.expect(mock.getBoolean()).andReturn(false); EasyMockHelper.replay(mock); assertFalse(mock.getBoolean()); } public void testExpectMultipleCalls() throws Exception { Sample mock = (Sample) EasyMockHelper.getMock(Sample.class); EasyMockHelper.expect(mock.getBoolean()).andReturn(false); EasyMockHelper.expect(mock.getBoolean()).andReturn(true); EasyMockHelper.replay(mock); assertFalse(mock.getBoolean()); assertTrue(mock.getBoolean()); } }
Even if above code contains some bugs and does not support all scenarios I think it proves that it should be quite easy to improve EasyMock Java 1.3 version.
Here is the source
Test your web design in different browsers
There is free online service available at:
http://browsershots.org/
where you can test your sites presentation under different browsers. Just submit the URL of your site and wait a bit -> you'll get the result as a set of screenshots soon.
There is another similar and simplier service available here:
http://www.browsrcamp.com/
It might be useful when you are working on Windows OS and want to test your website through the eyes of a Mac-user.
If you need an easy to set up console for Windows with tab support - consider Console2 (Open Source). The nicest thing about it is that you can select an actual console program that it will wrap about offering tabs, shortcuts, image or transparent backgrounds, etc on top of it. By default it uses cmd.exe, but if you have a cygwin (or mingw, etc.) installation, you can easily use bash, ash, etc. terminals.
There were some issues with older versions of Console2, I recommend to download the latest one (it is Beta, but anyway). Go to Edit->Settings->Tabs and set your bash, whatever terminal in the Shell if cmd.exe is not enough for you.
If you are using Rxvt terminal under Cygwin, then Console2 IMHO is a simpler, but more buggy thing, however it does offer you tabs and transparency out of the box. If you want to get tabs with Rxvt, you will have to run it under X server and recompile it with perl support to use a tab plugin plus some other hacking.
In my old [post] I came up with idea that we could implement EasyMock extension that enabled to use EasyMock 2 API with Java 1.4. Aleksei has done good work on this project and first version will soon be ready.
We think it would be cool if we made this library publicly available. However following issues need to be solved for that:
- what to publish
I think we should create one zip archive containing all artifacts like source, build script etc + compiled jar and dependencies. Whole package should be quite small so no point to create multiple separate packages. - where to publish
www.aqris.com - what license to use
EasyMock itself is licensed under MIT. I think we could use Eclipse Public License. It enables us to keep control if someone wants to improve/modify it. On the other hand it shouldn't be restrictive for users. - what artifacts need to be added to make everything covered with license?
I suppose this depends on selected license. Maybe for some including separate license file is enough while others recommend copyright text to be added to all source/other files? - where should we promote this library
Probably we should at least try to get link to our site from EasyMock homepage. Maybe there are other places where we could promote it?
When writing Hibernate DAOs (for the AMS especially, as there are tons of them) there were two things that always bothered me:
- these four methods that I had to write every single time: find(Serializable id), findAll(), save(Object entity), delete(Serializable id)
- @SuppressWarnings("unchecked") annotations everywhere
A couple of days ago it hit me: why not try and make life easier for such cases? So here's what came out in the end.
First I wrote a simple interface for our DAO interfaces to extend:
package dao; interface AbstractDao<T> { T find(Serializable id); List<T> findAll(); void save(T entity); void delete(Serializable id); }
Then an abstract class implementing this interface:
package dao; abstract class AbstractDaoImpl<T> implements AbstractDao<T> { protected final SessionFactory sessionFactory; public AbstractDaoImpl(final SessionFactory sessionFactory) { this.sessionFactory = sessionFactory; } public T find(final Serializable id) { final Class<T> targetClass = getValidTargetClass(); return (T) sessionFactory.getCurrentSession().load(targetClass, id); } public List<T> findAll() { return sessionFactory.getCurrentSession().createCriteria( getValidTargetClass()).list(); } public void save(final T entity) { sessionFactory.getCurrentSession().saveOrUpdate(entity); } public void delete(final Serializable id) { final Session s = sessionFactory.getCurrentSession(); final Object entity = s.load(getValidTargetClass(), id); s.delete(entity); } private Class<T> getValidTargetClass() { final Class<T> targetClass = getTargetClass(); if (targetClass == null) { throw new IllegalArgumentException("targetClass cannot be null"); } return targetClass; } protected abstract Class<T> getTargetClass(); }
Then the only things I need now to take advantage of the Fantastic Four methods are to get my DAO interfaces/classes to extend the aforementioned interface/class
package dao; public interface ProductDao extends AbstractDao<Product> { }
package dao; public class ProductDaoImpl extends AbstractDaoImpl<Product> implements ProductDao { public ProductDaoImpl(final SessionFactory factory) { super(factory); } @Override protected Class<Product> getTargetClass() { return Product.class; } }
And voila, now you can implement the methods that are specific for this particular DAO. I think that could cut down on a LOT of common code (judging by the AMS code again), but then again, of course you'd need Java 1.5 to take advantage of generics *sigh* — that's not something everyone can afford at the moment in their projects
.
Version 1 RC1 is fully functional and thoroughly tested version but we may need to change some things once we got licensing figured out.
In any case license will be something well known and with minimal restrictions so shouldn't cause any problems with customers.
If you have any concerns/questions let me know.