How to make instance of CrudRepository interface during testing in Spring? - java

I have a Spring application and in it I do not use xml configuration, only Java Config. Everything is OK, but when I try to test it I faced problems with enabling autowiring of components in the tests. So let's begin. I have an interface:
#Repository
public interface ArticleRepository extends CrudRepository<Page, Long> {
Article findByLink(String name);
void delete(Page page);
}
And a component/service:
#Service
public class ArticleServiceImpl implements ArticleService {
#Autowired
private ArticleRepository articleRepository;
...
}
I don't want to use xml configurations so for my tests I try to test ArticleServiceImpl using only Java Configuration. So for the test purpose I made:
#Configuration
#ComponentScan(basePackages = {"com.example.core", "com.example.repository"})
public class PagesTestConfiguration {
#Bean
public ArticleRepository articleRepository() {
// (1) What to return ?
}
#Bean
public ArticleServiceImpl articleServiceImpl() {
ArticleServiceImpl articleServiceImpl = new ArticleServiceImpl();
articleServiceImpl.setArticleRepository(articleRepository());
return articleServiceImpl;
}
}
In articleServiceImpl() I need to put instance of articleRepository() but it is an interface. How to create new object with new keyword? Is it possible without creating xml configuration class and enable Autowiring? Can autowired be enabled when using only JavaConfigurations during testing?

This is what I have found is the minimal setup for a spring controller test which needs an autowired JPA repository configuration (using spring-boot 1.2 with embedded spring 4.1.4.RELEASE, DbUnit 2.4.8).
The test runs against a embedded HSQL DB which is auto-populated by an xml data file on test start.
The test class:
#RunWith( SpringJUnit4ClassRunner.class )
#ContextConfiguration( classes = { TestController.class,
RepoFactory4Test.class } )
#TestExecutionListeners( { DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class,
TransactionDbUnitTestExecutionListener.class } )
#DatabaseSetup( "classpath:FillTestData.xml" )
#DatabaseTearDown( "classpath:DbClean.xml" )
public class ControllerWithRepositoryTest
{
#Autowired
private TestController myClassUnderTest;
#Test
public void test()
{
Iterable<EUser> list = myClassUnderTest.findAll();
if ( list == null || !list.iterator().hasNext() )
{
Assert.fail( "No users found" );
}
else
{
for ( EUser eUser : list )
{
System.out.println( "Found user: " + eUser );
}
}
}
#Component
static class TestController
{
#Autowired
private UserRepository myUserRepo;
/**
* #return
*/
public Iterable<EUser> findAll()
{
return myUserRepo.findAll();
}
}
}
Notes:
#ContextConfiguration annotation which only includes the embedded TestController and the JPA configuration class RepoFactory4Test.
The #TestExecutionListeners annotation is needed in order that the subsequent annotations #DatabaseSetup and #DatabaseTearDown have effect
The referenced configuration class:
#Configuration
#EnableJpaRepositories( basePackageClasses = UserRepository.class )
public class RepoFactory4Test
{
#Bean
public DataSource dataSource()
{
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
return builder.setType( EmbeddedDatabaseType.HSQL ).build();
}
#Bean
public EntityManagerFactory entityManagerFactory()
{
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setGenerateDdl( true );
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setJpaVendorAdapter( vendorAdapter );
factory.setPackagesToScan( EUser.class.getPackage().getName() );
factory.setDataSource( dataSource() );
factory.afterPropertiesSet();
return factory.getObject();
}
#Bean
public PlatformTransactionManager transactionManager()
{
JpaTransactionManager txManager = new JpaTransactionManager();
txManager.setEntityManagerFactory( entityManagerFactory() );
return txManager;
}
}
The UserRepository is a simple interface:
public interface UserRepository extends CrudRepository<EUser, Long>
{
}
The EUser is a simple #Entity annotated class:
#Entity
#Table(name = "user")
public class EUser
{
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO)
#Max( value=Integer.MAX_VALUE )
private Long myId;
#Column(name = "email")
#Size(max=64)
#NotNull
private String myEmail;
...
}
The FillTestData.xml:
<?xml version="1.0" encoding="UTF-8"?>
<dataset>
<user id="1"
email="alice#test.org"
...
/>
</dataset>
The DbClean.xml:
<?xml version="1.0" encoding="UTF-8"?>
<dataset>
<user />
</dataset>

If you're using Spring Boot, you can simplify these approaches a bit by adding #SpringBootTest to load in your ApplicationContext. This allows you to autowire in your spring-data repositories. Be sure to add #RunWith(SpringRunner.class) so the spring-specific annotations are picked up:
#RunWith(SpringRunner.class)
#SpringBootTest
public class OrphanManagementTest {
#Autowired
private UserRepository userRepository;
#Test
public void saveTest() {
User user = new User("Tom");
userRepository.save(user);
Assert.assertNotNull(userRepository.findOne("Tom"));
}
}
You can read more about testing in spring boot in their docs.

You cant use repositories in your configuration class because from configuration classes it finds all its repositories using #EnableJpaRepositories.
So change your Java Configuration to:
#Configuration
#EnableWebMvc
#EnableTransactionManagement
#ComponentScan("com.example")
#EnableJpaRepositories(basePackages={"com.example.jpa.repositories"})//Path of your CRUD repositories package
#PropertySource("classpath:application.properties")
public class JPAConfiguration {
//Includes jpaProperties(), jpaVendorAdapter(), transactionManager(), entityManagerFactory(), localContainerEntityManagerFactoryBean()
//and dataSource()
}
If you have many repository implementation classes then create a separate class like below
#Service
public class RepositoryImpl {
#Autowired
private UserRepositoryImpl userService;
}
In your controller Autowire to RepositoryImpl and from there you can access all your repository implementation classes.
#Autowired
RepositoryImpl repository;
Usage:
repository.getUserService().findUserByUserName(userName);
Remove #Repository Annotation in ArticleRepository and ArticleServiceImpl should implement ArticleRepository not ArticleService.

What you need to do is:
remove #Repository from ArticleRepository
add #EnableJpaRepositories to PagesTestConfiguration.java
#Configuration
#ComponentScan(basePackages = {"com.example.core"}) // are you sure you wanna scan all the packages?
#EnableJpaRepositories(basePackageClasses = ArticleRepository.class) // assuming you have all the spring data repo in the same package.
public class PagesTestConfiguration {
#Bean
public ArticleServiceImpl articleServiceImpl() {
ArticleServiceImpl articleServiceImpl = new ArticleServiceImpl();
return articleServiceImpl;
}
}

Related

How to inject pojo class in spring?

I have a pojo class which i need to inject in component. how to inject pojo object in spring?
For example
RestClass is under Module(like one microservice). Pojo Value should be injected here only. service class and pojo is different module.
#Controller
public class RestClass {
#Autowired
private ServiceClass serviceClass;
// How to apply MyConfig pojo object into ServiceClass
//like MyConfig.Builder().limit(100).build();
#PostMapping("/business")
private void callDoBusiness(){
serviceClass.doBusiness();
}
}
//// ServiceClass and Pojo class is under one module. Pojo object should not construct here. should be passed from another module
#Service
public class ServiceClass {
#Autowired
private MyConfig myConfig;
public void doBusiness(){
System.out.println(myConfig.getLimit())
}
}
#Builder
#Getter
public class MyConfig {
private int limit;
.
.
}
#Bean annotation is used in your case. For example:
Create some configuration file with #Configuration annotation. It doesn't matter where you create your Config configuration class or how you name it. You can even have multiple #Configuration classes across your project.
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
class Config {
// configure your MyConfig class here as a bean
#Bean
public MyConfig myConfig() {
return MyConfig.Builder().limit(100).build();
}
}
Now you can autowire it wherever you use it as in you example.
You can use #Configuration and #Bean. Something like this
AppConfig.java
#Configuration
public class AppConfig {
#Bean
public PojoClass getBean(){ return new PojoClass();}
}
You can use #Bean
#Configuration
public class Config {
#Bean
public MyConfig MyConfig() {
return new MyConfig();
}
}

how to load #Configuration classes in an order in spring boot

i would like to load #Configuration classes in an order. i have two configuration classes. i am having a requirement of loading my SampleProperties class before sampleconfiguration class.
I have tried the following annotations but it is not working as expected.
#AutoConfigureAfter(SampleProperties.class )
#AutoConfigureBefore(SampleConfiguration.class)
I have put my congiurations class in diff package in order to read configurations classes in an order.using #Import function, i am including my configuration classes into my application
My Main Class:
#Import({SampleProperties.class,SampleConfiguration.class,})
public class SampleApplication{
public static void main(String[] args) {
SpringApplication.run(SampleApplication.class, args);
}
}
My SampleProperties Class
#Configuration
#AutoConfigureBefore(SampleConfiguration.class)
#ConfigurationProperties("demo")
#Data
public class SampleProperties {
private String team;
private int teamSize;
private String teamLeader;
}
My sampleconfiguration Class:
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(entityManagerFactoryRef="sampleEntityManager",
transactionManagerRef="sampleTransactionManager",
basePackages= {"com.example.demo.repo"})
#AutoConfigureAfter(SampleProperties.class)
public class SampleConfiguration {
#Autowired
Environment env;
#Bean(name="sampleDataSource")
#Primary
public DataSource dmsDataSource() {
// functions
return null;
}
#Primary
#Bean(name = "sampleEntityManager")
public LocalContainerEntityManagerFactoryBean dmsEntityManagerFactory(EntityManagerFactoryBuilder builder) {
// functions
return null;
}
#Primary
#Bean(name = "sampleTransactionManager")
public PlatformTransactionManager dmsTransactionManager(#Qualifier("sampleEntityManager") EntityManagerFactory entityManagerFactory) {
// functions
return null;
}
}
can anyone tell me what missing and where am making mistakes?
I think you have to use #Order annotation.
#Component
#Order(1)
public class SampleProperties {
// code
}
#Component
#Order(2)
public class SampleConfiguration {
// code
}

Spring boot data JPA getting NullPointerException related to transaction manager

At the outset, I have tried the options mentioned in various forums for the same stack trace I get. A few of them did not work while with others (like removing javax.persistence.transactiontype) I did not understand how and where to try it.
I am using Spring Boot data JPA (1.2 RC2) + Hibernate (with a custom persistence.xml). Here is my Application.java
#Configuration
#ComponentScan(<our package>)
#EnableAutoConfiguration(exclude = EmbeddedServletContainerAutoConfiguration.class)
#EnableTransactionManagement
#DependsOn("transactionManager")
#EnableJpaRepositories(transactionManagerRef = "transactionManager")
public class Application {
public static void main(String[] args) {
run(Application.class, args);
}
}
My RepositoryConfiguration (as we have custom persistence.xml - currently need to reuse it)
#Configuration
public class RepositotyConfiguration {
#Autowired
private DataSource dataSource;
#Value("${db.dialect}")
private String dialectClass;
#Bean(name = "entityManagerFactory")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(EntityManagerFactoryBuilder builder) {
LocalContainerEntityManagerFactoryBean entityManagerFactory = builder.dataSource(dataSource).
persistenceUnit("main").build();
entityManagerFactory.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
Properties additionalProperties = new Properties();
additionalProperties.put("hibernate.dialect", dialectClass);
entityManagerFactory.setJpaProperties(additionalProperties);
return entityManagerFactory;
}
#Bean(name = "transactionManager")
public PlatformTransactionManager transactionManager(LocalContainerEntityManagerFactoryBean entityManagerFactoryBean) {
JpaTransactionManager txManager = new JpaTransactionManager();
txManager.setEntityManagerFactory(entityManagerFactoryBean.getObject());
return txManager;
}
}
The transactionManager here is created if I do not have a single Repository but the moment I add one, ny tests fail with this exception:
Caused by: java.lang.NullPointerException
at org.hibernate.engine.transaction.internal.jta.JtaStatusHelper.getStatus(JtaStatusHelper.java:76)
at org.hibernate.engine.transaction.internal.jta.JtaStatusHelper.isActive(JtaStatusHelper.java:118)
at org.hibernate.engine.transaction.internal.jta.CMTTransaction.join(CMTTransaction.java:149)
My test application context is:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
#WebAppConfiguration
#EnableAutoConfiguration
#TransactionConfiguration(transactionManager = "transactionManager")
#IntegrationTest("server.port:0")
#ActiveProfiles("test")
public abstract class TestApplicationContext {
#Autowired
private WebApplicationContext wac;
#Value("${local.server.port}")
private int port;
...
}
An example (not the actual) of the repository I try to add (where let's say Item is a model object)
public interface ItemRepository extends CrudReposity<Item, Long> {
Item findByCode(String code); // this seems to cause the problem, assume 'code' is field in Item
}
Any pointers will be of utmost help.
EDIT: It now fails only if I add extra method in ItemRepository say Item findByItemCode(String itemCode) where let's say itemCode is a field in Item model, but can't understand why?
Thanks,
Paddy

Spring data testing custom repository data doesn't update

I am trying to write a test for custom spring data repository. I'm also using QueryDSL.
I am new to spring-data. I use spring support for HSQL DB in testing. MySQL for dev.
Problem: I do not see updated data in tests if I use custom repository.
public interface AuctionRepository extends AuctionRepositoryCustom, CrudRepository<Auction, Long>, QueryDslPredicateExecutor<Auction> {
// needed for spring data crud
}
.
public interface AuctionRepositoryCustom {
long renameToBestName();
}
.
public class AuctionRepositoryImpl extends QueryDslRepositorySupport implements AuctionRepositoryCustom {
private static final QAuction auction = QAuction.auction;
public AuctionRepositoryImpl() {
super(Auction.class);
}
#Override
public long renameToBestName() {
return update(auction)
.set(auction.name, "BestName")
.execute();
}
}
My test
Somehow fails at last line
public class CustomAuctionRepositoryImplTest extends AbstractIntegrationTest {
#Inject
AuctionRepository auctionRepository;
#Test
public void testDoSomething() {
Auction auction = auctionRepository.findOne(26L);
assertEquals("EmptyName", auction.getName());
// test save
auction.setName("TestingSave");
auctionRepository.save(auction);
Auction saveResult = auctionRepository.findOne(26L);
assertEquals("TestingSave", saveResult.getName());
// test custom repository
long updatedRows = auctionRepository.renameToBestName();
assertTrue(updatedRows > 0);
Auction resultAuction = auctionRepository.findOne(26L);
assertEquals("BestName", resultAuction.getName()); // FAILS expected:<[BestNam]e> but was:<[TestingSav]e>
}
}
I can't figure out why data doesn't update when using custom repository. If I start application in dev mode, and call renameToBestName() through controller, everything works as expected, name changes.
Below is Test Configuration if needed
#RunWith(SpringJUnit4ClassRunner.class)
#Transactional
#ActiveProfiles("test")
#ContextConfiguration(classes = {TestBeans.class, JpaConfig.class, EmbeddedDataSourceConfig.class})
#ComponentScan(basePackageClasses = IntegrationTest.class, excludeFilters = #Filter({Configuration.class}))
public abstract class AbstractIntegrationTest {
}
.
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(basePackageClasses = Application.class)
class JpaConfig {
#Value("${hibernate.dialect}")
private String dialect;
#Value("${hibernate.hbm2ddl.auto}")
private String hbm2ddlAuto;
#Value("${hibernate.isShowSQLOn}")
private String isShowSQLOn;
#Autowired
private DataSource dataSource;
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean entityManagerFactory = new LocalContainerEntityManagerFactoryBean();
entityManagerFactory.setDataSource(dataSource);
entityManagerFactory.setPackagesToScan("auction");
entityManagerFactory.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
Properties jpaProperties = new Properties();
jpaProperties.put(org.hibernate.cfg.Environment.DIALECT, dialect);
if ( !hbm2ddlAuto.isEmpty()) {
jpaProperties.put(org.hibernate.cfg.Environment.HBM2DDL_AUTO, hbm2ddlAuto);
}
jpaProperties.put(org.hibernate.cfg.Environment.SHOW_SQL, isShowSQLOn);
jpaProperties.put(org.hibernate.cfg.Environment.HBM2DDL_IMPORT_FILES_SQL_EXTRACTOR, "org.hibernate.tool.hbm2ddl.MultipleLinesSqlCommandExtractor");
entityManagerFactory.setJpaProperties(jpaProperties);
return entityManagerFactory;
}
#Bean
public PlatformTransactionManager transactionManager() {
return new JpaTransactionManager();
}
}
This is due to the update query issued through your code is defined to not evict the object potentially touched by the query from the EntityManager. Read more on that in this answer.

#Autowire Not Setting DAO

I have the following Spring Java configuration set up:
#Configuration
public class FooConfig {
#Autowired
private IReferenceDataDAO referenceDataDAO;
#Bean
public IReferenceDataService getReferenceDataService() {
return new ReferenceDataServiceImpl(referenceDataDAO);
}
}
I am trying to reference the ReferenceDataService bean in another configuration class using the #Autowire property:
#Configuration
public class BarConfig {
#Autowired
private IRuleService ruleService;
#Autowired
private IReferenceDataService referenceDataService;
}
Here is the config that defines the IReferenceDataDAO:
#Configuration
public class FooBarConfig {
#Bean
public IReferenceDataDAO getReferenceDataDAO() {
return new ReferenceDataDAOImpl(getStaticData(), getMapper());
}
}
Here is the ReferenceDataServiceImpl:
public class ReferenceDataServiceImpl implements IReferenceDataService {
private IReferenceDataDAO dao;
public ReferenceDataServiceImpl(IReferenceDataDAO dao) {
this.dao = dao;
}
The above configs are imported in a master config class:
#Configuration
#Import({
FoosAppConfig.class,
BarAppConfig.class,
FooBarAppConfig.class,
})
I am noticing that this configuration leads to the referenceDAO in the ReferenceDataServiceImpl being set to null. What am I doing wrong? Shouldn't that #Autowire annotation ensure that my bean is fully configured before setting it?
BarConfig needs to import FooConfig
#Import({ FooConfig.class })
Try this:
#Configuration
#Import({ FooConfig.class })
public class BarConfig {
#Autowired
private IRuleService ruleService;
#Autowired
private IReferenceDataService referenceDataService;
}
This allows BarConfig to get access to the IReferenceDataService bean.

Categories

Resources