Testing with Testng and EasyMock

I had my brush with testng and easymock some time back. It’s quite cutting edge technology and works with java 5 and above only since it relies heavily on some java 5 features such as annotations and the assert keyword.
I shall be describing a slightly unusual scenario in this post.
EasyMock works with interfaces and requires that all your classes implement an interface. However there is an extension of EasyMock available that works with classes as well.

Here are some useful links for testng and easymock before you attempt something serious:

http://testng.org/doc/documentation-main.html
http://easymock.org/EasyMock2_2_Documentation.html

You need to downlaod the testng and easymock jars and have them on your classpath before you run this example. The following link describes how you can plugin testng to your eclipse installation:

http://testng.org/doc/eclipse.html

Here are the classes I used:

Bean.java

class Bean implements IBean {
	private int i;

	public Bean(int i) {
		this.i = i;
	}

	public int getBeanId() {
		return i;
	}
};

IBean.java

public interface IBean {
	public int getBeanId();
}

BeanInspectorFactory.java

class BeanInspectorFactory implements IBeanInspectorFactory {
	public BeanInspector getBeanInspectorInstance() {
		return new BeanInspector();
	}
};

IBeanInspectorFactory.java

public interface IBeanInspectorFactory {

	public IBeanInspector getBeanInspectorInstance();
}

BeanInspector.java

class BeanInspector implements IBeanInspector {
    public void inspect(IBean bean) {
        System.out.println("Bean " + bean.getBeanId()
                + " successfully inspected");
    }
};

IBeanInspector.java

public interface IBeanInspector {
	public void inspect(IBean bean);
}

InspectorThread.java – The class to test

public class InspectorThread implements Runnable {
	private IBeanInspectorFactory beanInspectorFactory;

	private IBeanInspector beanInspector;

	private IBean bean;

	public InspectorThread(IBeanInspectorFactory beanInspectorFactory,
			IBean bean) {
		this.beanInspectorFactory = beanInspectorFactory;
		this.bean = bean;
	}

	public void run() {
		beanInspector = beanInspectorFactory.getBeanInspectorInstance();
		beanInspector.inspect(bean);
	}
};

InspectorThreadInvoker.java

public class InspectorThreadInvoker {
	public static void main(String[] args) {
		Bean bean = new Bean(1);
		BeanInspectorFactory beanInspectorFactory = new BeanInspectorFactory();
		InspectorThread t = new InspectorThread(beanInspectorFactory, bean);
		Thread thread = new Thread(t);
		thread.start();
	}
}

BeanInspectorTest.java – The test class uses testng and easymock

import org.easymock.EasyMock;
import org.easymock.IMocksControl;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import static org.easymock.EasyMock.*;

public class BeanInspectorTest {
	IMocksControl mockControl;

	/**
	 * Create Mock Control before the tests.
	 */
	@BeforeClass
	public void setUp() {
		mockControl = (IMocksControl) EasyMock.createStrictControl();
	}

	/**
	 * Reset the Mock Control after each test.
	 */
	@AfterTest
	public void cleanUp() {
		mockControl.reset();
	}

	@Test
	public void testInspection() {
		IBeanInspectorFactory mockbeanInspFact = mockControl
				.createMock(IBeanInspectorFactory.class);
		IBeanInspector mockBeanInspector = mockControl
				.createMock(IBeanInspector.class);
		IBean mockbean = mockControl.createMock(IBean.class);
		expect(mockbeanInspFact.getBeanInspectorInstance()).andReturn(
				mockBeanInspector);
		mockBeanInspector.inspect(mockbean);
		mockControl.replay();
		InspectorThread t = new InspectorThread(mockbeanInspFact, mockbean);
		Thread thread = new Thread(t);
		thread.start();
		mockControl.verify();
	}
}

You also need an xml file for configuration but it need not be named testng.xml as I have done:

testng.xml


 

    
        
            
        
    

The test runs successfully and prints out:

[Parser] Running:
D:workprojectsGreatMockGreatMocktestng.xml


===============================================
Suite1
Total tests run: 1, Failures: 0, Skips: 0
===============================================


Testng expects the lines between mockControl.replay(); and mockControl.verify(); to emulate the mock actions before mockControl.replay();

For example if I modified the test method with a minor change in the call sequence .. viz.

@Test
	public void testInspection() {
		IBeanInspectorFactory mockbeanInspFact = mockControl
				.createMock(IBeanInspectorFactory.class);
		IBeanInspector mockBeanInspector = mockControl
				.createMock(IBeanInspector.class);
		IBean mockbean = mockControl.createMock(IBean.class);
		mockBeanInspector.inspect(mockbean);
		expect(mockbeanInspFact.getBeanInspectorInstance()).andReturn(
				mockBeanInspector);
		// mockBeanInspector.inspect(mockbean);
		mockControl.replay();
		InspectorThread t = new InspectorThread(mockbeanInspFact, mockbean);
		Thread thread = new Thread(t);
		thread.start();
		mockControl.verify();
	}

I get the following error: (Note I have made the call to inspect() before it is actually done in the actual code.)

[Parser] Running:
D:\work\projects\GreatMock\GreatMock\testng.xml
Exception in thread "Thread-0" java.lang.AssertionError:
Unexpected method call getBeanInspectorInstance():
inspect(EasyMock for interface IBean): expected: 1, actual: 0
at org.easymock.internal.MockInvocationHandler.invoke(MockInvocationHandler.java:32)
at org.easymock.internal.ObjectMethodsFilter.invoke(ObjectMethodsFilter.java:61)
at $Proxy6.getBeanInspectorInstance(Unknown Source)
at InspectorThread.run(InspectorThread.java:15)
at java.lang.Thread.run(Unknown Source)
===============================================
Suite1
Total tests run: 1, Failures: 1, Skips: 0
===============================================

The application can be downloaded here

Validating the sex field

It was a boring day at the office. We were doing some validation stuff on a couple of web pages. The team clown was doing some UI work on a page that had a textfield asking the user to enter his/ her sex. The valid options were Male, Female, Not Applicable.
The validation stuff done, the clown was trying to come up with an error message to display to the user on invalid entry.
“Sex is required”, he muttered to himself.
The team wit laughed out loud and repeated, “Of course, SEX is required!”.
The clown then said, how about “Sex is mandatory?”
“Yeah”, echoed the wit, “SEX is mandatory.”
Now we were all in splits.
Suddenly the team whiz came running into the cubicle with breaking news. “The requirements have changed. Sex is no longer mandatory, it is optional.”
We all cried out loud in unision. “Of course, SEX is not required, it is optional.”
So much for boring work hours.

Migrating to newer versions of Jboss – the pains..

It is ironic how fast technology changes and programs written in older software become obsolete and have to be migrated to the newer technologies with some effort, sometimes due to a loss of backward compatibility. This applies to the tutorials written on this blog as well. One point for example was the JDOM parser example that I had to rewrite for the new JDOM API http://cuppajavamattiz.com/2009/03/19/using-jdom-to-map-an-xml-to-a-pojo-bean/ JBoss is one software that adapts to the latest J2EE technologies faster than any of the other existing J2EE application servers, as soon as a new J2EE spec is released. This sometimes causes a lot of heartache when you find that a program written for an earlier version of JBoss no longer works on the newer one.
I tried to run the pagination example at http://cuppajavamattiz.com/2007/05/21/web-design-patterns-at-work-with-pagination/, but found that I was running into problems either at JBoss startup or at runtime. Let me describe some of the pains I had to take.
I downloaded the latest version of JBoss(jboss-5.0.1.GA-jdk6.zip) and tried to run the pagination example described in the link above with the same source code and jar deployments.
The first problem I had was a Jboss startup error:
ConfigurationException: CONFIGURATION FAILED! ..class..cannot be cast to..class..
Thinking this was a version problem with the JSF jars that I was using, I experimented with various versions of the jar files, until I found the problem: JBoss has its own set of JSF jars under jboss-5.0.1.GA\server\default\deploy\jbossweb.sar\jsf-libs in its new avatar and the jars in my war/lib were clashing with those already on the JBoss server.
So I replaced my older JSF jars in the WAR with those in the Jboss installation.
The older verions of the tlds too were not compatible with JBoss, as I got the following exception:
Failed to load or instantiate TagExtraInfo class: com.sun.faces.taglib.FacesTagExtraInfo
Next I found that JBoss could not locate JSF tlds under WEB-INF. It apparently did not look for the relevant tlds inside the JSF jar files:
Could not locate tld files under WEB-INF.
I found that the tld files under WEB-INF of my war file had to be replaced by the tld files in the META-INF folder of the jboss JSF jars. This done, the problem too vanished.
I packaged the the MySQL driver that I was using within the lib folder of my WAR file only to get:
Apparently wrong driver class specified for URL: class: com.mysql.jdbc.Driver
The problem was that I had forgotten that JBoss looks for the database drivers under jboss-5.0.1.GA\server\default\lib and not in the WAR file.
This done, the application successfully executed without a hitch.
Whew!
Just for documentation, here is the version of mysql-ds.xml that I used.(to be put in the deploy folder of jboss)

<?xml version="1.0" encoding="UTF-8"?>
<datasources>
    <local-tx-datasource>
        <jndi-name>dbpool</jndi-name>
        <connection-url>jdbc:mysql://localhost:3306/AUTHORS</connection-url>
        <driver-class>com.mysql.jdbc.Driver</driver-class>
        <user-name>admin</user-name>
        <password>admin</password>
        <metadata>
            <type-mapping>mySQL</type-mapping>
        </metadata>
    </local-tx-datasource>
</datasources>