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", ""); } }
0 Comments:
Post a Comment
Subscribe to Post Comments [Atom]
<< Home