Unit test of a spring scheduler configuration using Mockito.spy

Hello,

here is a little sample on how to write scheduled monitoring unit test using spring and mockito spy factory method.


In the legacy code there is a job that is defined to purge the database each 8 hour using a spring defined quarrtz job:


scheduled-service-context.xml


<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
          xmlns="http://www.springframework.org/schema/beans"
          xsi:schemalocation="http://www.springframework.org/schema/beans  
               http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">  
   <bean class="com.foo.ScheduledDatabasePurgeMonitor"
            id="scheduledDatabasePurgeMonitor">  
 </bean></beans>  

scheduled-trigger-context.xml

 <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xsi:schemalocation="http://www.springframework.org/schema/beans  
               http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">  
   <bean class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean" id="scheduledDatabasePurgeJob">  
     <property name="targetObject" ref="scheduledDatabasePurgeMonitor">  
     <property name="targetMethod" value="doIt">  
     <property name="concurrent" value="false">  
   </property></property></property></bean>  
   <bean class="org.springframework.scheduling.quartz.SimpleTriggerBean" id="databasePurgeTrigger">  
     <!-- see the example of method invoking job above -->  
     <property name="jobDetail" ref="scheduledDatabasePurgeJob">  
     <!-- first start after 1 milliseconds -->  
     <property name="startDelay" value="1">  
     <!-- repeat every (schedule.databasePurge.repeatIntervalMs) milliseconds / default 8 hours -->  
     <property name="repeatInterval" value="${schedule.databasePurge.repeatIntervalMs:28800000}">  
   </property></property></property></bean>  
   <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">  
     <property name="triggers">  
       <list>  
         <ref bean="databasePurgeTrigger">  
       </ref></list>  
     </property>  
   </bean>  
   <!-- official howto :  
      http://static.springsource.org/spring/docs/3.0.5.RELEASE/reference/scheduling.html#scheduling-quartz  
    -->  
 </beans>  

here is the sample purgemonitor class:


 /**  
  * ScheduledDatabasePurgeMonitor  
  */  
 public class ScheduledDatabasePurgeMonitor {  
   private final static Logger logger = LoggerFactory.getLogger(ScheduledDatabasePurgeMonitor.class.getName());  
   public void doIt() {  
     logger.debug("database purge right now");  
     purgeDatabase();  
   }  
   void purgeDatabase() {  
     logger.debug("* purge database *");  
   }  
 }  

we would like to ensure in an unit test that the spring configuration enable the purge scheduling each N ms :

  • we would like a little N to avoid an 8 hour unit test
  • we would like to mock the purge service


So we need a sping spy'ed bean and here is a way to do this :
Unit test class :


 package com.foo;  
 import org.junit.Assert;  
 import org.junit.Before;  
 import org.junit.Ignore;  
 import org.junit.Test;  
 import org.junit.runner.RunWith;  
 import org.slf4j.Logger;  
 import org.slf4j.LoggerFactory;  
 import org.springframework.beans.factory.annotation.Autowired;  
 import org.springframework.test.context.ContextConfiguration;  
 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;  
 import static org.mockito.Mockito.spy;  
 import static org.mockito.Mockito.times;  
 import static org.mockito.Mockito.verify;  
 /**  
  * ScheduledDatabasePurgeMonitorTest IT Test of the database purge monitoring  
  */  
 @RunWith(SpringJUnit4ClassRunner.class)  
 @ContextConfiguration  
 public class ScheduledDatabasePurgeMonitorTest {  
      private final static Logger logger = LoggerFactory.getLogger(ScheduledDatabasePurgeMonitorTest.class.getName());  
   @Autowired  
   ScheduledDatabasePurgeMonitor scheduledDatabasePurgeMonitor;  
   @Autowired  
   private Integer purgeRepeatIntervalMs;
   @Before
   public void setup() {
       // avoid the database purge here
       doNothing().when(scheduledDatabasePurgeMonitor).purgeDatabase();
   }  
      /**  
       * GIVEN ScheduledDatabasePurgeMonitor implementation WHEN scheduler  
       * trigger THEN purge service is called  
       *   
    */  
      @Test  
      public void database_purge_monitor_should_be_periodically_invoked() {  
     // THEN  
     logger.info("verify first call of purgeDatabase");  
     int callNumber = 1;  
     verify(scheduledDatabasePurgeMonitor, times(callNumber)).purgeDatabase();  
     for (int i=0; i<3 ; i++) {  
       waitForNextScheduledUpdate();callNumber++;  
       logger.info("verify next call ({}) of purgeDatabase", callNumber);  
       verify(scheduledDatabasePurgeMonitor, times(callNumber)).purgeDatabase();  
     }  
      }  
   private void waitForNextScheduledUpdate() {  
     try {  
       long waitIntMs = getPurgeRepeatIntervalMs() + 100;  
       logger.debug("wait for {} seconds", (waitIntMs / 1000));  
       Thread.sleep(waitIntMs);  
     } catch (InterruptedException e) {  
       Assert.fail("InterruptedException oO");  
     }  
   }  
   public ScheduledDatabasePurgeMonitor getScheduledDatabasePurgeMonitor() {  
     return scheduledDatabasePurgeMonitor;  
   }  
   public void setScheduledDatabasePurgeMonitor(ScheduledDatabasePurgeMonitor scheduledDatabasePurgeMonitor) {  
     this.scheduledDatabasePurgeMonitor = scheduledDatabasePurgeMonitor;  
   }  
   public Integer getPurgeRepeatIntervalMs() {  
     return purgeRepeatIntervalMs;  
   }  
   public void setPurgeRepeatIntervalMs(Integer purgeRepeatIntervalMs) {  
     this.purgeRepeatIntervalMs = purgeRepeatIntervalMs;  
   }  
 }  

Unit test spring context :

 <?xml version="1.0" encoding="UTF-8"?>  
 <beans xmlns="http://www.springframework.org/schema/beans"  
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">  
   <import resource="classpath:/META-INF/spring/jasypt-context.xml"/>  
   <bean class="org.jasypt.spring31.properties.EncryptablePropertySourcesPlaceholderConfigurer" depends-on="jasyptPrerequisites">  
     <constructor-arg ref="configurationEncryptor"/>  
     <property name="locations">  
       <list>  
         <value>classpath:credentials.properties</value>  
         <value>classpath:/com/foo/scheduled-database-purge.properties</value>  
       </list>  
     </property>  
   </bean>  
   <bean id="toSpyScheduledDatabasePurgeMonitor"  
      class="com.foo.ScheduledDatabasePurgeMonitor"/>  
   <bean id="scheduledDatabasePurgeMonitor"  
      class="org.mockito.Mockito" factory-method="spy">  
     <constructor-arg ref="toSpyScheduledDatabasePurgeMonitor" />  
   </bean>  
   <import resource="classpath:META-INF/spring/services-scheduled-trigger-context.xml"/>  
   <bean id="purgeRepeatIntervalMs" class="java.lang.Integer">  
     <constructor-arg value="${schedule.databasePurge.repeatIntervalMs}"/>  
   </bean>  
 </beans>  

unit test properties file :

# test purpose : ask iaas quota each 2 seconds
schedule.databasePurge.repeatIntervalMs=2000

you would notice that Mockito.spy(object) static method is used into the spring unit test context :

   <bean id="scheduledDatabasePurgeMonitor"  
      class="org.mockito.Mockito" factory-method="spy">  
     <constructor-arg ref="toSpyScheduledDatabasePurgeMonitor" />  
   </bean>  

Aucun commentaire:

Enregistrer un commentaire