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: ,

0 Comments:

Post a Comment

Subscribe to Post Comments [Atom]

<< Home