About Me

My Photo
A Sun certified Java professional with time proven experience in architecture and designing of mid/large scale Java/JEE based applications. Creator of the EasyTest Framework.A lot of experience with technologies such as Spring framework, Hibernate , JPA, Java4-6, REST, SOA , YUI , JUnit , Cloud Foundry PaaS and other technologies.

Sunday, May 19, 2013

Writing cleaner Unit and Integration tests in Java

EasyTest Framework provides a lot of features to write clean and maintainable Unit/Integration tests for your  application. You can externalize the test data, you can generate test reports, you can measure the performance of the class under test, you can pass input parameters to the test methods, you can return data from your test methods that is captured and stored for you and much more. This blog post will delve deeper into one of its feature that allows the test writer to externalize any test configurations from the actual test class. We will look at how it helps in writing clean and maintainable tests.

To get started, you need to download the latest version of EasyTest-Core module from teh Maven Central Repository.


<dependency>
    <groupId>org.easetech</groupId>
    <artifactId>easytest-core</artifactId>
    <version>1.1</version>
</dependency>

If you want a quick view of what EasyTest Framework is and what it provides, have a look at this Blog Entry.Once you have the dependency, you are set to start writing your Unit Tests with configurations sitting outside of the actual test.There are 3 main annotations that help you in configuring your test beans outside the actual tests.
1) @TestConfigProvider - This annotation is used on the test class to identify where the configuration resides.

2) @TestBean - This is a method level annotation applied on the methods in the Config class(which is defined by @TestConfigProvider) to identify the test beans that should be injected into your test class.

3) @TestProperties - This is a class level annotation that provides convenient mechanism to load properties in your config class.Time for some examples.
We will look at a very simple example for this blog post to introduce you to the idea of IoC in EasyTest and how to externalize your test configurations. You can then take it to any level of detail in your respective projects.
Lets assume I have a Service by the name ItemService that I want to test. Now this item service requires a lot of initialization before it can be used. Furthermore, lets assume that this service is used in more than one test. So each time I have to use it, I have to duplicate the code across tests. This makes my test filled with logic that ideally is not my test's concern. My test should worry about what method to test on the Service rather than how to initialize it.In the traditional world of testing, you would write a static method annotated with BeforeClass annotation and initialize it inside the test. In EasyTest world, the test is the main focus, not the configurations that goes around it. Let's see how we can leverage the above annotations to make our tests simpler to write and easier to maintain.

Step 1: We use the @TestConfigProvider annotation at the class level on the Test Class to tell the EasyTest Framework where to potentially get the Test Configurations from.

@RunWith(DataDrivenTestRunner.class)
@TestConfigProvider({TestConfigProviderClass.class})
public class TestBeanConfigurations {

//Your test logic like @Before methods @Test methods etc 

}
In the above example, @TestConfigProvider is used which takes an array of Class objects. In the above example the array consists of a single entry withe the name : TestConfigProviderClass Next lets look at the TestConfigProviderClass itself

1-> @TestProperties({"config.properties", "anotherConfig.properties"})
public class TestConfigProviderClass {

private Properties props;

2-> @TestBean("itemService") public RealItemService itemService(){
String propValue = props.getProperty("simple.property");
System.out.println(propValue);
return new RealItemService();
}
}

 As you can notice, TestConfigProviderClass is a simple class that has two special annotations. Let's first talk about @TestBean annotation.

@TestBean annotation is used to define a bean that could be injected into any test class. We will look at the example of how to inject this bean very soon.

@TestBean optionally takes a String that identifies the name of the bean. Test Classes can then ask for a configured bean by its name. If no name is provided, then the resolution of the bean happens by Type.

In the above case we are defining a single bean with the name "itemService".

Another annotation that we have used is @TestProperties at the class level. This annotation, along with the props class variable of type Properties is used to load properties from a properties file. The user has to define the annotation and declare a class variable of type Properties (name of the variable doesnt matter). EasyTest will then automatically load all the properties from the listed properties file into the defined class variable. This variable can then be used inside methods annotated with @TestBean. In the above example, we are loading the properties from two files : config.properties and anotherConfig.properties. We are also using the loaded property with the name "simple.property" inside the itemService method.

We now have all the setup ready. All that is left to do is bind these together. Our test class should be able to now simply get the instance of RealItemService. Lets see how it can do the same.

@RunWith(DataDrivenTestRunner.class)
@TestConfigProvider({TestConfigProviderClass.class})
public class TestBeanConfigurations {
->@Inject
->@Named("itemService")
public ItemService testSubject;

@Test
@DataLoader(filePaths={"overrideExcelData.csv"})
public Item getExcelTestDataWithDouble(@Param(name="libraryId") Double libraryId, @Param(name="itemId")Double itemId) {
Assert.assertNotNull(testSubject); Item item = testSubject.findItem(new LibraryId(Long.valueOf(libraryId.longValue())),new ItemId(Long.valueOf(itemId.longValue()))); return item; }//end method }//end class

We are using standard CDI annotations @Inject and @Named to Inject a test bean into our test class. EasyTest recognises these annotation and honors them. In case you dont want to use standard CDI annotations for some reason, then you could also use the @Provided annotation which is provided by the EasyTest framework. If we omit the @Named annotation above, then the bean injection will happen by Type.

Thats it. Simple and fast. If you want to know more about the features provide by the EasyTest Framework, visit the GitHub site of EasyTest Core module and EasyTest Spring module.

No comments: