Spring Java configuration to override and mock services using Mockito

Wednesday, March 5, 2014

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: Lets 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", "");
}

}



Related Posts by Categories

0 comments:

Post a Comment