Google

Nov 21, 2013

Spring Java configuration to override and mock services using Mockito

In one of the previous posts entitled  Spring Java based configuration using @Configuration and @Bean we discussed how to use Java based configuration in Spring. We also looked at Mockito based examples and BDD using jBehave in this site.


Step 1: Let's say we have a AccountService class that we want to subject under test. While testing, we want to supply mocks for  BankAccountService and CashTransactionService. But use the real CalcEngine.


package com.myapp.services;

import static com.jpmorgan.wss.aes.calculation.strongersuper.engine.CalcEngineDroolsHelper.isNull;
//..

import java.io.IOException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

import javax.annotation.Resource;

import org.joda.time.DateTime;
import org.joda.time.LocalDate;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;


@Component("accountService")
public class AccountService
{
  
   
    @Resource(name = "cashTransactionService")
    private CashTransactionService cashTransactionService;
    
        
    @Resource(name = "bankAccountService")
    private BankAccountService bankAccountService;
    
            
    @Resource(name = "calcEngine")
    private CalcEngine calcEngine;
    
      
    @Transactional(readOnly = true)
    public List<balances> calcTransaction(int accountId)
            throws IOException
    {
        //..............uses  cashTransactionService, bankAccountService, and calcEngine
    }
    
    //...other methods
    
}


Step 2: The jBehave step class with Spring DI.

package com.myapp.bdd.steps;

import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.drools.runtime.StatelessKnowledgeSession;
import org.hamcrest.MatcherAssert;
import org.hamcrest.Matchers;
import org.jbehave.core.annotations.Given;
import org.jbehave.core.annotations.Then;
import org.jbehave.core.annotations.When;
import org.jbehave.core.model.ExamplesTable;
import org.joda.time.DateTime;
import org.mockito.Mockito;
import org.springframework.stereotype.Component;

@Component
public class CashTransactionRulesStep
{
    
    @Resource(name = "accountService")
    private AccountService accountService;
    
    @Resource(name = "cashTransactionService")
    private CashTransactionService cashTransactionService;
    
    @Resource(name = "bankAccountService")
    private BankAccountService bankAccountService;

    BankAccount bankAccount = new BankAccount();
 List<CashTransaction> cashTransactionsList = Collections.EMPTY_LIST;
 List<Transaction> result = null;
       
    @Given("a bankAccountCd = $bankAccountCd and bankAccountNm = $bankAccountNm")
    public void bankAccountDetails(String bankAccountCd, String bankAccountNm)
    {
        bankAccount.setAccountCd(bankAccountCd);
        bankAccount.setAccountNm1(bankAccountNm);
    }
    
    @When("calcTransaction method is fired with portfoliocd = $portfolioCode")
    public void calcTransaction(String portfolioCode)
    {
        
        try
        {
           
            Mockito.when(
                    cashTransactionService.findByAccountDt(Mockito.anyInt(), (Date) Mockito.anyObject(),
                            (Date) Mockito.anyObject())).thenReturn(cashTransactionsList);
            
            Mockito.when(
                    bankAccountService.getBankAccount(Mockito.anyInt())).thenReturn(bankAccount);
            
            result = transactionService.calcTransaction(1);
             
        }
        catch (Exception e)
        {
            throw new RuntimeException(e);
        }
        
    }
    
    //......
}

Step 3: Now the Spring config class that overrides DI in AccountService with fully and partially mocked injection. The resource names need to be same to override, and nor defined under  @ComponentScan but defined with @Bean.

package com.myapp.bdd.stories;

import org.mockito.Mockito;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.context.annotation.Import;

@Configuration("AccountingStoryConfig")
@ComponentScan(
        basePackages =
        {
            "com.myapp.bdd",
            "com.myapp.accounting",
          
        
        },
        useDefaultFilters = false,
        includeFilters =
        {
            @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = ValidatorChain.class),
            @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = TransactionValidator.class)
        })
@Import(
{
    StoryConfig.class //import other cobfigs
})
public class AccountingStoryConfig
{
    
    @Bean(name = "accountService")
    public TransactionService getAccountService()
    {
        return Mockito.spy(new AccountServiceImpl()); //partially mock
    }
    
    @Bean(name = "cashTransactionService")
    public CashTransactionService getCashTransactionService()
    {
        return Mockito.mock(CashTransactionServiceImpl.class); //fully mock
    }
    
    @Bean(name = "bankAccountService")
    public BankAccountService getBankAccountService()
    {
        return Mockito.mock(BankAccountService.class); //fully mock
    }
    
}


Step 4: Finally, for completion sake, the jBehave story class that can run as jUnit test.

package com.myapp.bdd.stories;

import java.util.List;

import org.jbehave.core.io.CodeLocations;
import org.jbehave.core.io.StoryFinder;
import org.springframework.batch.core.configuration.support.ClassPathXmlApplicationContextFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class AccountStory extends AllStories
{
    
    public AccountStory()
    {
        super();
    }
    
    protected ApplicationContext context()
    {
        if (context == null)
        {
            try
            {
                ClassPathXmlApplicationContextFactory factory = new ClassPathXmlApplicationContextFactory(null);
                factory.setApplicationContext(new AnnotationConfigApplicationContext(AccountStoryConfig.class));
                
                context = factory.createApplicationContext();
            }
            catch (Exception e)
            {
                throw new RuntimeException(e);
            }
        }
        return context;
    }
    
    @Override
    protected List<string> storyPaths()
    {
        return new StoryFinder().findPaths(
                CodeLocations.codeLocationFromClass(getClass()), "**/account.story", "");
    }
    
}



Labels: ,

Jun 27, 2013

Java unit testing with mock objects using frameworks like Mockito.

This is an extension to my blog post entitled  unit testing Spring MVC controllers for web and RESTful services with spring-test-mvc.  The purpose of this is to demonstrate the power of using mock objects via frameworks like Mockito.

This example tests a Spring MVC Controller used for servicing RESTful services, and this example illustrates a CSV file download.

 
@Controller
public class MyAppController
{

    @Resource(name = "aes_cashForecastService")
    private MyAppService myAppService;

    @RequestMapping(
            value = "/portfolio/{portfoliocd}/detailsPositionFeed.csv",
            method = RequestMethod.GET,
            produces = "text/csv")
    @ResponseBody
    public void getPositionFeedCSV(
            @PathVariable(value = "portfoliocd") String portfolioCode,
            @RequestParam(value = "valuationDate", required = true) @DateTimeFormat(pattern = "yyyyMMdd") Date valuationDate,
            HttpServletResponse response) throws Exception
    {
       
        try
        {
            
            PositionFeedCriteria criteria = new PositionFeedCriteria();
            criteria.setEntityCd(portfolioCode);
            criteria.setValuationDtTm(valuationDate != null ? valuationDate
                    : new Date());
            
            String fileName = "test.csv"
            
   //headers for file download
            response.addHeader("Content-Disposition", "attachment; filename="+ fileName);
            response.setContentType("text/csv");
            //returns results as CSV
            String positionFeedCSV = myAppService.getPositionFeedCSV(criteria);
            response.getWriter().write(positionFeedCSV);
            
        }
        catch (Exception e)
        {
           e.printStackTrace();
        }
        logger.info("Completed producing position feed CSV");
    }
 
  public void setMyAppService(MyAppService myAppService)
    {
        this.myAppService = myAppService;
    }
    
}


Now comes the junit class using the Mockito framework to test the MyAppController.

 
//...

import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;

public class MyAppControllerTest
{
     private static final String PORTFOLIO_CODE = "abc123";
   private static final java.util.Date VALUATION_DATE = new java.util.Date();

     private MyAppService mockMyAppService;
  private MyAppController controller;

  @Mock
     HttpServletResponse response;
  
  @Before
     public void setup()
     {
        MockitoAnnotations.initMocks(this);
        controller = new MyAppController();
        mockMyAppService = mock(MyAppServiceImpl.class);
        controller.setMyAppService(mockMyAppService);
     }
  
  //The test case
  @Test
     public void testGetPositionFeedCSV() throws Exception
     {
        String str = "dummyCSV";
        //Set up behavior
        when(mockMyAppService.getPositionFeedCSV(any(PositionFeedCriteria.class))).thenReturn(str);
        when(response.getWriter()).thenReturn(writer);
        
        //Invoke controller
        controller.getPositionFeedCSV(PORTFOLIO_CODE, VALUATION_DATE, response);
        
        //Verify behavior
        verify(mockMyAppService, times(1)).getPositionFeedCSV(any(PositionFeedCriteria.class));
        verify(writer, times(1)).write(any(String.class));
     } 
}
   

The key point to note here is the ability of the mock objects to verify if a particular method was invoked and if yes, how many times was invoked. This is demonstrated with the last two lines with the verify statement. This is one of the key differences between using a mock object versus a stub. This is a common Java interview question quizzing the candidate's understanding of the difference between a mock object and stub.

Labels: , , ,

Jun 17, 2013

JUnit with Mockito tutorial

Unit testing is very important in development life cycle, and you can expect questions regarding mock objects and unit testing in general. This post extends the blog post relating to writing a mapper class for Apache Camel. This post writes a JUnit test with Mockito for the PersonMapper class.


Step 1: Have the relevant dependencies required to write unit tests.

 
<dependency>
 <groupId>junit</groupId>
 <artifactId>junit</artifactId>
 <version>4.10</version>
 <scope>test</scope>
</dependency>
<dependency>
 <groupId>org.mockito</groupId>
 <artifactId>mockito-core</artifactId>
 <version>1.8.5</version>
 <scope>test</scope>
</dependency>


Step 2: The class that is being unit tested. The PersonMapper.java



 
package com.mycompany.app5;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.apache.camel.Exchange;
import org.springframework.stereotype.Component;

@Component("personMapper")
public class PersonMapper
{
    
    private static final String HEADER_STATUS = "status";
    private static final String HEADER_DEST_FILE_SUFFIX = "dest_file_suffix";
    
    enum Status
    {
        accepted, rejected
    };
    
    public Object service(List<List<String>> data, Exchange exchange)
    {
        List<Map<String, String>> results = new ArrayList<Map<String, String>>();
        
        Map<String, String> headerLine = new LinkedHashMap<String, String>();
        headerLine.put("surname", "surname");
        headerLine.put("firstname", "firstname");
        headerLine.put("age", "age");
        
        results.add(headerLine);
        
        boolean accepted = true;
        
        for (List<String> line : data)
        {
            Map<String, String> resultLine = new LinkedHashMap<String, String>();
            resultLine.put("surname", line.get(1));
            resultLine.put("firstname", line.get(0));
            resultLine.put("age", line.get(2));
            results.add(resultLine);
            
            if (line.get(1) == null || line.get(2) == null)
            {
                accepted = false;
            }
            
        }
        
        if (accepted)
        {
            exchange.getIn().setHeader(HEADER_STATUS, Status.accepted.name());
        }
        else
        {
            exchange.getIn().setHeader(HEADER_STATUS, Status.rejected.name());
        }
        
        String srcFileName = (String) exchange.getIn().getHeader("CamelFileNameOnly");
        exchange.getIn().setHeader(HEADER_DEST_FILE_SUFFIX, srcFileName);
        
        return results;
        
    }
}


Step 3: The unit test itself with mock objects. The Exchange and Message objects from Apache Camel are mocked using the Mockito framework. Here is the PersonMapperTest class.

 
package com.mycompany.app5;

import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;

import junit.framework.Assert;

import org.apache.camel.Exchange;
import org.apache.camel.Message;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.Mockito;

public class PersonMapperTest
{
    
    @Mock
    Exchange mockExchange;
    
    @Mock
    Message mockMessage;
    
    PersonMapper personMapper;
    
    @Before
    public void setup()
    {
        mockExchange = mock(Exchange.class);
        mockMessage = mock(Message.class);
        personMapper = new PersonMapper();
        
        when(mockExchange.getIn()).thenReturn(mockMessage);
        //invoking a void method
        doNothing().when(mockMessage).setHeader(Mockito.anyString(), Mockito.anyString());
        //mock return values
        when(mockMessage.getHeader("CamelFileNameOnly")).thenReturn("capitalexpenses_20130510.csv");
        
    }
    
    @Test
    public void testService()
    {
        @SuppressWarnings("unchecked")
        List<LinkedHashMap<String, String>> result = (List<LinkedHashMap<String, String>>) personMapper.service(
                getPersonRecords(), mockExchange);
        
        Assert.assertNotNull(result);
        Assert.assertEquals(2, result.size());
        LinkedHashMap<String, String> headerRec = result.get(0);
        LinkedHashMap<String, String> record = result.get(1);
        String headerStr = headerRec.values().toString();
        String recordStr = record.values().toString();
        
        Assert.assertEquals(
                "[surname, firstname, age]",
                headerStr
                );
        
        Assert.assertEquals(
                "[Smith, John, 35]",
                recordStr
                );
    }
    
    private List<List<String>> getPersonRecords()
    {
        List<List<String>> values = new ArrayList<List<String>>(5);
        
        List<String> asList = Arrays.asList(new String[]
        {
            "John", "Smith", "35"
        });
        values.add(asList);
        return values;
    }
}

Labels: , ,

Mar 28, 2013

Apache Camel asynchronous processing unit testing

In the previous post entitled Apache Camel for asynchronous processing demonstrates the power of Apache camel. In this post, I will provide a sample code that unit tests the Apache camel.

package com.myapp.camel;

import static org.mockito.Matchers.any;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;

import com.myapp.config.MyappConfig;
import com.myapp.model.FeedGenerationRequest;
import com.myapp.model.JobType;
import com.myapp.service.MyappService;

import java.util.ArrayList;
import java.util.List;

import org.apache.camel.CamelContext;
import org.apache.camel.Produce;
import org.apache.camel.ProducerTemplate;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.spring.javaconfig.CamelConfiguration;
import org.apache.camel.spring.javaconfig.test.JavaConfigContextLoader;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests;

@ContextConfiguration(
        locations =
        {
            "com.myapp.camel.JobHandlingRouteBuilderTest$ContextConfig"
        },
        loader = JavaConfigContextLoader.class)
public class JobHandlingRouteBuilderTest extends AbstractJUnit4SpringContextTests
{
    
    @Produce(uri = DIRECT_START)
    private ProducerTemplate template;
    
    private static final String DIRECT_START = "direct:start";
    
    @Mock
    FeedGenerationRequest request;
    
    private static MyappService MyappService = mock(MyappService.class);
    
    @Before
    public void setUp()
    {
        MockitoAnnotations.initMocks(this);
    }
    
    @Test
    @Ignore
    public void testConfigureJobHandlingRoute() throws Exception
    {
        template.sendBodyAndHeader(request,
                JobHandlingRouteBuilder.JOB_TYPE_HEADER, JobType.FEED_GENERATION);
        Thread.sleep(1000);
        verify(MyappService, times(1)).produceGroupLevelCashFeed(any(FeedGenerationRequest.class));
        verify(MyappService, times(1)).produceGroupLevelPositionFeed(any(FeedGenerationRequest.class));
    }
    
    @Test
    @Ignore
    public void testConfigureJobHandlingRouteOnFailure() throws Exception
    {
        doThrow(new Exception()).when(MyappService).produceGroupLevelCashFeed(any(FeedGenerationRequest.class));
        template.sendBodyAndHeader(request,
                JobHandlingRouteBuilder.JOB_TYPE_HEADER, JobType.FEED_GENERATION);
        Thread.sleep(1000);
        verify(MyappService, times(1)).markControllableJobFailed(any(FeedGenerationRequest.class),
                any(String.class));
    }
    
    @Configuration
    @Import(
    {
        MyappConfig.class
    })
    public static class ContextConfig extends CamelConfiguration
    {
        @Bean
        public MyappService myappService()
        {
            return MyappService;
        }
        
        private MyappConfig MyappConfig;
        
        @Autowired
        public void setMyappConfig(MyappConfig MyappConfig)
        {
            this.MyappConfig = MyappConfig;
        }
        
        @Bean
        public RouteBuilder route()
        {
            return new RouteBuilder()
            {
                @Override
                public void configure()
                {
                    from(DIRECT_START)
                            .to(JobHandlingRouteBuilder.JOB_QUEUE);
                }
            };
        }
        
        @Override
        public List<routebuilder> routes()
        {
            List<routebuilder> routes = new ArrayList<routebuilder>();
            MyappConfig.setCamelContext(mock(CamelContext.class));
            routes.add(MyappConfig.jobHandlingRouteBuilder());
            routes.add(route());
            return routes;
        }
        
    }
    
}



The MyappConfig class will be as shown below.

package com.myapp.config;

import com.myapp.camel.JobHandlingRouteBuilder;
import com.myapp.service.MyappForecastService;

import javax.annotation.Resource;

import org.apache.camel.CamelContext;
import org.apache.camel.CamelContextAware;
import org.apache.camel.ProducerTemplate;
import org.apache.camel.builder.RouteBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


@Configuration
public class MyappConfig implements CamelContextAware
{
    
    private CamelContext context;
    
    @Resource(name = "myappService")
    private MyappService myappService;
    
    @Bean
    public ProducerTemplate template()
    {
        if (context != null)
        {
            return context.createProducerTemplate();
        }
        //For unit tests
        else
        {
            return null;
        }
    }
    
    @Bean
    public RouteBuilder jobHandlingRouteBuilder()
    {
        return new JobHandlingRouteBuilder(cashForecastService);
    }
    
    @Override
    public void setCamelContext(CamelContext camelContext)
    {
        context = camelContext;
    }
    
    @Override
    public CamelContext getCamelContext()
    {
        return context;
    }
    
}



Labels: , , ,