Project

General

Profile

IntegrationTests » History » Revision 4

Revision 3 (Andreas Kohlbecker, 02/05/2013 10:47 AM) → Revision 4/10 (Andreas Kohlbecker, 02/05/2013 10:48 AM)

{{>toc}} 


 ----- 


 # Testing java code using Maven and Unitils 



 ## Configuration files 


 There are two    [Unitils](http://www.unitils.org/summary.html)     configuration files in the cdmlib project which contain among other stuff the database connection and the like for    the Unitils _!DatabaseModule_ _DatabaseModule_ and _!DbUnitModule_: _DbUnitModule_: 

 * **cdmlib-io:** `/cdmlib-io/src/test/resources/unitils.properties`  

 * **cdmlib-persistence:** `/cdmlib-persistence/src/test/resources/unitils.properties`  

 
 HINT: _To access the H2 data base you have to start the server at some point in the code and you have to set a breakpoint of course because the data base only exists during the runtime of the test _ (see also #3293) 


 ---- 

 _Everything below this line may be **OUTDATED** and needs to be reviewed._ 


 ---- 


 If we're following good test driven design practices, then we should be writing tests before we write code and running them often to demonstrate that our code works. It is important that tests: 


 * Run fast (they have short setups, run times, and break downs).  

 * Run in isolation (you should be able to reorder them).  

 * Use data that makes them easy to read and to understand.  

 * Use real data (e.g. copies of production data) when they need to.  

 * Represent one step towards your overall goal.  


 [Maven](http://maven.apache.org) gets us some of the way towards this by providing a standard build cycle, integration with surefire, a testing harness that isolates the classes under test in their own boot loader to ensure that only dependencies specified are present in the test environment, dependency management, etc. 


 Nevertheless, it is still easy to write tests that don't follow the principles outlined above. One way to make your life easier is to use some libraries to provide boilerplate code (i.e. code which you're likely to use from one test to the next) to make it simpler and easier to do complicated things within your test. One really interesting tool is [Unitils](http://www.unitils.org) which is a meta-framework which ties several testing api's together. An example of a simple unit test using unitils is as follows: 


 ~~~ 
 public class SimpleTest extends UnitilsJUnit4 { 
  
     private ClassUnderTest classUnderTest; 
     private String testData; 
    
     @Before 
     public void setup() { 
         classUnderTest = new ClassUnderTest(); 
         testData = "Lorem"; 
     } 

     @Test 
     public void testMethod() { 
         String result = classUnderTest.method(testData); 

         assertEquals("Ipsum",result); 
     }   
 }  

 ~~~ 

 There is one thing which is worth noticing about the test above – it sets up test data outside the test itself. There are two reasons for this. Firstly, its better to set up the objects needed and the data required outside of the test itself if you can. It makes the test easier to read, and by using @@Before@, which is run prior to every test, the test data is restored to the same state at the beginning of each test. This isn't always possible, but if you need to initialize many objects prior to a test, this might mean your test is too complicated and needs to be broken down further.  


 Unitils only really begins to show a benefit, however, when you want to do something more ambitious than a straightforward unit test. 



 ## Testing persistence-related functionality 


 If we want to test the persistence layer in the cdm properly, we need spring (our DAO's are spring beans), hibernate (our favourite ORM tool), and a test database with data in it. We want to be able to insert data into the database before we run a test, and if we modify the state of the database during a test, we want to check its state afterwards. [DBUnit](http://dbunit.sourceforge.net/) is a tool to help us do this, and unitils makes it easy to write tests using dbunit, hibernate and spring. 


 To run an integration test using hibernate, substitute the normal datasource (e.g. `org.springframework.jdbc.datasource.DriverManagerDataSource@) with an instance of @org.unitils.database.UnitilsDataSourceFactoryBean` in your spring application context. This bean picks up the configuration held in a file called unitils.properties (see source:"trunk/cdmlib/cdmlib-persistence/src/test/resources/unitils.properties") located somewhere in your classpath. To specify the application context to use in the test, use the annotation `@SpringApplicationContext` e.g. 


 ~~~ 
 @SpringApplicationContext("path/to/your/applicationContext.xml") 
 public class PersistenceTest extends UnitilsJUnit4 { 
  
   @SpringBeanByType 
     private DaoUnderTest daoUnderTest; 
     private String testData; 
    
     @Before 
     public void setup() { 
         testData = "Lorem"; 
     } 

     @Test 
     @DataSet("PersistenceTest.testMethod.xml") 
     @ExpectedDataSet 
     public void testMethod() { 
         String result = daoUnderTest.method(testData); 

         assertEquals("Ipsum",result); 
     }   
 } 

 ~~~ 

 Unitils will autowire properties annotated with `@SpringBeanByType` and `@SpringBeanByName` with beans from your application context. Annotating the class or individual test methods with `@DataSet("datasetname.xml")` results in unitils loading a dbunit dataset into the test database prior to the test starting up. If the test class is located in eu/etaxonomy/cdmlib/persistence/ then the data set needs to be located there too. `@ExpectedDataSet` checks the state of the database at the end of the test against a data set file located in package/ called `TestClassName.testMethodName-result.xml` . There is no such convention for @@DataSet@, but I follow the same convention any way, naming dataset input files @TestClassName.testMethod.xml@.  



 ## Testing service and controller layer classes using mocks 


 If you don't depend upon external code that is too complicated to replicate otherwise, you can avoid using spring and hibernate To fully isolate the class under test, you can inject mock objects into the dependencies of a class under test, to verify its behaviour without creating real objects which can act as a point of failure outside of the class under test. [EasyMock](http://www.easymock.org/) is a framework that provides mock objects, and provides tools for verifying their behaviour. 

 ~~~ 
 public class ControllerTest extends UnitlsJUnit4 { 
     @Mock 
     @InjectInto(property = "service") 
     private Service mockService 

     @TestedObject 
     private TestController controllerUnderTest; 

     private String testData; 

    @Before 
     public void setup() { 
         testData = "Lorem"; 
         mockService = EasyMock.createMock(Service.class); 
         controllerUnderTest = new TestController(); 
     } 

     @Test 
     public void testMethod() { 
         EasyMock.expect(mockService.serviceMethod(EasyMock.eq(testData))).andReturn("Ipsum"); 
         EasyMock.replay(mockService); 

         String result = controllerUnderTest.method(testData); 

         assertEquals("Ipsum",result); 
         EasyMock.verify(mockService) 
     }   
 } 
 ~~~ 

 In the example above, there is an instance (declared using an interface) _mockService_ which we're not interested in testing and an instance _controllerUnderTest_ which we do want to unit test. The behaviour of `TestController` which we're testing depends upon an instance of `Service` being injected in to our controller. By annotating our mock with `@Mock` and `@InjectInto` and annotating our class under test with @@TestedObject@, unitils handles the dependency injection for us. 


 We still need to create our mock, using the static method @EasyMock.createMock(Interface.class)@. We then tell easymock what behaviour we expect of our mocks (in this case, we expect the method _serviceMethod_ to be called, and we expect the argument to be equal to _testData_). We want our mock to return the value "Ipsum" when this method is called. 


 Once we've finished defining the expected behaviour of our mocks we call `EasyMock.replay(mock1, mock2, . . .)@. We then run our test. After the test has finished, we call @EasyMock.verify(mock 1, mock2, . . . )` to verify that the expected behaviour occurred.