-
Notifications
You must be signed in to change notification settings - Fork 15
EasyTest : An Introduction
EasyTest is a Data Driven Testing Framework which is based on JUnit framework. You can download the latest version of EasyTest(Core and Spring) from the Maven Central Repository
EasyTest aims to provide its users a convenient mechanism to perform Data Driven testing and at the same time keeping it intuitive.
Here's an overview (with details below) of what EasyTest provides:
-
Write Data Driven Tests where data can be specified externally using Excel, CSV, XML or any other user defined format.
-
Pass input data to test cases in the form of method parameters using @Param annotation
-
Return Data from test cases and capture the test output in a file
-
Input parameters passed to the test method can be of type (Java types, user defined type). EasyTest does not restrict its users to pass only certain specific types to the test method.
-
Data can be loaded both at the Class Level as well as at the method level using DataLoader annotation. This gives users the ability to override the class level data at the method level.
-
Multiple types (CSV,Excel,XML,custom) files can be used in the same test class to load the test data.
-
User can Inject their test beans to their test classes using @Provided annotation on the Field whose value should be injected.
-
Users can Intercept the calls to the class under test and do things like performance monitor their class methods and other valuable things.
-
The IDE outputs information about which method was run with which input parameters and with what input values. This is extremely useful in quickly identifying what went wrong in the test case and also to get clear picture.
-
EasyTest supports Spring Test framework and you can run your Spring tests using SpringTestRunner of EasyTest which will give you all the above facilities.
-
A user can provide test data file as a classpath resource(classpath: prefix), filesystem resource or a Url Resource.
And there are more things that you can explore while working with EasyTest.
A user of EasyTest framework can mark the test methods with @Test annotations, just like in JUnit. But unlike JUnit, a user can pass input data to the test method in the form of an XML (or Excel or CSV or custom defined) file and the framework will make sure that the right test method gets called for the right set of test data.
This is just an overview and EasyTest provides a rich feature set for its users to perform various levels of testing that have become part of today's testing world.
Here are few things that EasyTest provides:
- A user can externalize the test data in a file, instead of defining it in the test class itself. This keeps the test data and the test methods separate.
Heres an example of how it can be done:
@RunWith(DataDrivenTestRunner.class)
@DataLoader(filePaths = { "org/easetech/data/testData.csv" })
public class TestClass {
.........
As you can see above we are using @RunWith provided by JUnit and running it with EasyTest defined Runner called DataDrivenTestRunner. Additionally we have annotation @DataLoader that a user uses to define the input test data for the test class.
-
Another major shift away from traditional Testing frameworks is that EasyTest provides its users the ability to pass parameters to the test method. So a user can now define a test method like this:
@Test public void testWithInputParameter(@Param(name = "libraryId")Float libraryId) { .......... }
@Param identifies for the EasyTest framework which input parameters needs to be passed to the input test method.
-
A user can also pass custom Property Editor based property objects to the test method. So for example, if you have an Id object LibraryId that has a property Editor associated with it, you can simply pass that object to the test method like this:
@Test public void testWithRegistryPropertyParameter(LibraryId libraryId) { .......... }
Note in this case you can omit the @param annotation alltogether, if you want. You can find more details on how PropertyEditor support works in EasyTest on its wiki page.
- A user can also pass a custom object to the test method even if it is not a Registry Editor based object. In that case, a user has to provide a custom converter that knows how to convert data into the custom object. Thus the test class will look like this:
You can register the converters in two ways:
@BeforeClass
public static void setUp() {
//ItemConverter knows how a Map of key value pair can be converted into a custom Item Object.
ConverterManager.registerConverter(ItemConverter.class);
}
@RunWith(DataDrivenTestRunner.class)
@DataLoader(filePaths = { "org/easetech/data/testData.csv" })
@Converters({ItemConverter.class})
public class TestClass {
.........
Once you have registered the Converters, you can use then use the object as part of the method input parameter.
@Test
public void testItemConverter(Item item) {
Assert.assertNotNull(item);
}
- If you are writing test cases for a complex service, you will realize that a LOT of work in the test Class is done only for setting up the Class that needs to be tested. For e.g. mocking its dependencies, setting values, initializing the class and bringing it to an initial testable state. It makes the Test class complicated and polluted with initialization code that should ideally reside outside of the test case. EasyTest now provides a facility to Inject fields into your test class. This is achieved using the concept of Inversion of Control. You declare your test beans in a config class outside of your test cases. These config beans can be used by multiple test classes. You then tell your test class from where to fetch the config beans using @TestConfigProvider annotation on your test class specifying the class object of the test Config class. Finally you declare which fields in the test class should be provided by the EasyTest framework using @Provided annotation. Here's an example of how you can do it.
First declare your TestBean in a config class :
public class TestConfigProviderClass {
@TestBean("itemService") public RealItemService itemService(){
return new RealItemService();
}
}
Next, use it in your test class :
@RunWith(DataDrivenTestRunner.class)
@TestConfigProvider({TestConfigProviderClass.class})
public class TestBeanProviderFunctionality {
@Inject
public ItemService itemService;
EasyTest supports standard Java CDI annotations @Inject, @Named as well as its own custom annotation @Provided. Besides the standard Injection by name and injection by Type, EasyTest also supports injection by fieldName. The above is an example of Injection by fieldName where the name of the field is the same as the name of the Bean.
-
Sometimes, it becomes necessary to override test data at the method level. EasyTest provides that facility.
@RunWith(DataDrivenTestRunner.class) @DataLoader(filePaths = { "getTestData.xls" }) public class TestConditionsSupportedByDataDrivenTest { @Test @DataLoader(loader = CustomObjectDataLoader.class) public void testCustomDataLoader(@Param()Item item) { Assert.assertNotNull(item); } @Test public void testClassLevelDataLoader(Map<String, Object> inputData) { ............ }
In the above scenario, testClassLevelDataLoader test method will get its input test data from getTestData.xls file whereas testCustomDataLoader will get its input test data from CustomObjectDataLoader class.
-
EasyTest moves away from custom Testing frameworks in one more aspect. It provides its user with the facility to return the test data from the test method. This is extremely useful in cases where you want to compare the test results of the test methods that are run on different platforms. For e.g. you are currently using JDBC for your DAO class and you have tests written for that. Now you decided to move towards JPA. You need to confirm that your application is behaving as expected. What you do is you first run your EasyTest enabled test methods against JDBC based implementation. Then run your tests against JPA based implementation and compare the results. NOTE: EasyTest saves any data that the test method returns into the same file as was used by the test class to load the test data for that method. An example of how you can do it is:
@Test @DataLoader(filePaths = { "org/easetech/data/test-combined.xls" }) public Item getDataWithReturnType(@Param(name = "libraryId")Float libraryId, @Param() ItemId itemId) { return new Item();
} In this case, the returned data will be written to the file org/easetech/data/test-combined.xls under the heading "ActualResult".
-
There are scenarios when a user wants to measure how much time his service is actually taking to return the test data. This is useful in cases where you want to do first level of performance analysis and need to get a rough idea on which services might be slow. EasyTest provides a convenient annotation (@Intercept) support for such a scenario.
@RunWith(DataDrivenTestRunner.class) @DataLoader(filePaths = { "org/easetech/data/testExcelData.xls" }) @TestConfigProvider({TestConfig.class}) public class TestExcelDataLoader { @Intercept @Provided public RealItemService itemService;
What this does is it simply calls the EasyTestDefaultInterceptor which simply prints the time taken in nanoseconds by the actual business service call on the console. You can use the annotation for both Interface as well as concrete type classes.
Obviously, it would be useless, if it did only this much. Interestingly it gives a lot more facility to its users.
A User can specify @Intercept(interceptor="MyCustomInterceptor.class") to specify the custom behavior that may be required in his particular scenario. Thus user can drive this annotation and thus gives them a lot of power.
- Another thing that EasyTest prides in is displaying the user which test method is run with what input test data (both the input key and the input value). Thus in the IDE that supports JUnit, the test methods are displayed in the following format: testMethodOne{libraryId=123, itemId=456} testMethodOne{libraryId=908, itemId=897} testMethodTwo{libraryId=123, itemId=87}
Note that the first two methods have the same name but different input data. EasyTest handles creating extra and seperate test methods for separate test data.
EasyTest supports Spring 3.0.6.RELEASE and above.
Thus a user can use EasyTest framework's SpringTestRunner to run DataDrivenTest in Spring Integration test environment.
A simple way to do it would be like this:
@RunWith(SpringTestRunner.class)
@ContextConfiguration(classes = { XmlBusinessConfig.class }, loader =AnnotationConfigContextLoader.class)
@DataLoader(filePaths = { "input-data.xml" })
@TransactionConfiguration(transactionManager="transactionManager" )
@Transactional
public class TestSpringConfiguration {
@Autowired @SpringIntercept(interceptor = EasyTestDefaultInterceptor.class)
public ItemService testSubject;
@BeforeClass
public static void setUp() {
PropertyEditorManager.registerEditor(ItemId.class, ItemIdEditor.class);
}
@Test
public void testSimple(){
...........
}
@Test
public void getItemsDataUsingXMLLoader(@Param(name="searchText") String searchText,
@Param(name="itemType") String itemType, @Param() ItemId itemId , @Param(name="expectedItems") int expectedItems) {
System.out.println(testSubject == null);
List<Item> items = testSubject.getItems(itemId, searchText, itemType);
Assert.assertNotNull(items);
Assert.assertEquals(expectedItems, items.size());
}
}
You will notice that we are using Spring provided Annotations @ContextConfiguration, @TransactionConfiguration and @Transactional. And instead of using SpringJunit4ClassRunner, we are using EasyTest's SpringTestRunner. And we are using the same @DataLoader annotation to load our test data. This will enable Data Driven Testing in Spring managed projects. Thus if you want to migrate from your current Spring Tests to EasyTest, all you have to do is use SpringTestRunner and provide input test data using @DataLoader and you will be good to go.
Also notice that we are using @SpringIntercept instead of @Intercept annotation along with @Autowired annotation. When using SpringIntercept annotation, EasyTest framework will make sure that the injected Spring bean gets instrumented one more time to intercept service methods for the end user as well. Isn't it amazing? The main difference between @Intercept and @SpringIntercept is that @Intercept uses CGLib to proxy classes whereas @SpringIntercept uses Spring-AOP support to intercept classes as well as interfaces. Thus you can Intercept both a concrete type field as well as Interface with SpringIntercept annotation. You can't do that with Intercept annotation.
This page gives an overview of potentially what all you can do with EasyTest. It does not yet give you the complete picture of what all it is capable of. For more indepth understanding, look at the WIKI pages for each of the functionality that EasyTest provides.