Showing posts with label override. Show all posts
Showing posts with label override. Show all posts

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

}



Read More..