I'm developing REST App for the IT courses. One of the requirements was to use Spring-JDBC with all queries to be stored in separate properties file. Here is a part of my UserDaoImpl:
#Repository
#RequiredArgsConstructor
#PropertySource(value = "classpath:/user_queries.properties")
#Log
public class UserDaoImpl implements UserDao {
private final NamedParameterJdbcTemplate template;
private final RowMapper<User> rowMapper;
#Value("${find.by_id}")
private String findById;
#Override
public Optional<User> findById(int id) {
SqlParameterSource param = new MapSqlParameterSource("id", id);
User user = null;
try {
user = template.queryForObject(findById, param, BeanPropertyRowMapper.newInstance(User.class));
} catch (DataAccessException ex) {
String.format("User with id - %d, not found.", id);
}
return Optional.ofNullable(user);
}
}
All method work fine, but troubles started when I wrote my Test to this class:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = {TestDBConfiguration.class})
public class UserDaoTest {
#Autowired
DataSource dataSource;
#Test
public void findByIdTest() {
NamedParameterJdbcTemplate template = new NamedParameterJdbcTemplate(dataSource);
UserRowMapper userMapper = new UserRowMapper();
UserDao userDao = new UserDaoImpl(template, userMapper);
User user = new User(1, "John", "Doe",
"$2a$04$MzVXtd4o0y4DOlyHMMLMDeE4/eezrsT5Xad.2lmGr/NkCpwBgvn3e",
"jdoe#gmail.com", true);
assertEquals(Optional.of(user), userDao.findById(1));
}
}
When I run this Test my findById is always null. Is there any way to run this test and keep all queries inside the separate file?
Since your UserDaoImpl uses Spring annotations (#Value, #PropertySource, etc), you have to initialize it via Spring to enable their injection. Or else you'd have to supply all these values yourself manually.
So in your test you should inject the DAO itself:
#Autowired
private UserDao userDao;
Or if you want to go manual way (not recommended), allow setting field findById via constructor (recommended) same way you're passing template, userMapper.
Related
I'm trying to write a test for one of my services. The service uses autowired repository which uses jdbcTemplate to access the DB. The problem is that the test actually puts data in the real DB.
Here is my test class:
#SpringApplicationConfiguration(Application.class)
#SpringBootTest(classes = { UserServiceImpl.class, UserService.class })
#RunWith(SpringJUnit4ClassRunner.class)
public class UserServiceTest {
#Autowired UserService userService;
#Test
public void test() {
final String fName = " xxxxxx ";
User user = new User();
user.setFirstName(fName);
user.setLastName(fName);
user.setEmail(fName);
user.setLogin(fName);
user.setPhone(fName);
userService.create(user);
user = userService.getUserByLogin(fName).get();
assertEquals(fName, user.getLogin());
}
}
Is there anything i can do to prevent the userService to work with real DB and somehow just make a simulation?
Best option is to use different DataSource bean in tests. You application would be tested against some in memory DB (typically H2). Just create this test configuration somewhere in your src/test/java/yourRootPackage:
#TestConfiguration
public class DataSourceConfig {
#Bean
#Primary
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.generateUniqueName(true)
.setType(H2)
.build();
}
}
I have following CrudRepository and Dao service. For testing purpose I am using EmbeddedDatabaseBuilder(creating tables and inserting sample data).
In that case as you can see from the RDBMSDaoImpl->test method, when I try to access the data in PROPERTY table via SpringJPA it returns empty result.
But when I use the convertional way(jdbctemplate) I get value that inserted via insert-data.sql.
Using Spring boot 1.3.3.RELEASE.
public interface OfPropertyRepository extends CrudRepository <OfProperty, String> {
OfProperty findByName(String name);
}
#Service
public class RDBMSDaoImpl implements RDBMSDao {
private static final String SQL = "SELECT VALUE FROM TIMS.PROPERTY WHERE ID=?";
#Autowired
OfPropertyRepository OfPropertyRepository;
#Override
public int test() {
**//Return a value**
String value = jdbcTemplate.queryForObject(SQL, String.class, "prop.key");
**// no value found**
OfPropertyRepository.findAll();
}
////
public class AppConfigTest {
#Bean(destroyMethod = "shutdown")
public DataSource getDataSource() throws Exception {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.setName("TIMS")
.addScript("classpath:/db/create-db.sql")
.addScript("classpath:/db/insert-data.sql")
.build();
}
}
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {AppConfigTest.class})
#ActiveProfiles("Test")
public class BaseSendServMessageRequestTest implements ApplicationContextAware{}
Adding missing bean described here
https://geowarin.github.io/using-spring-data-jpa-in-a-java-se-environment-and-run-tests-with-dbunit.html
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.
By using JMockit #Capturing, it was unable to capture call to any spring data jpa repository methods.
public interface UserRepository extends JpaRepository<UserEntity, Long> {
UserEntity findByName(String name);
}
public class DefaultUserService implements UserService {
public User getUser(Long id) {
return userRepo.findOne( id );
}
public User getUser(String name) {
return userRepo.findByName( name );
}
}
public class UserServiceTest extends AbstractServiceTest {
#Inject UserService userService;
**#Capturing UserRepository userRepo;**
#Test
public void getUser_ShouldReturnUserObject() {
**new Expectations() {{
userRepo.findByName(anyString);
result = new UserEntity(1l, null, null, null, null);
}};**
User user = userService.getUser("abc");
assertNotNull(user.getId());
assertEquals(1l, user.getId().longValue());
}
}
However by replacing
UserRepository
with
JpaRepository<UserEntity, Long>
in the test class, JMockit able to intercept call to any methods available in JpaRepository interface like findOne() or findAll().
But it just not able to capture calls to custom repository methods that extends JpaRepository such as findByName().
I prefer to use #Capturing for this scenario even though JMockit state-based testing such as MockUp and Deencapsulation can solve this issue because it is much more simpler.
Any one has any ideas to solve this issue?
Without knowing what AbstractServiceTest + Spring is doing with respect to the #Inject fields, I can't tell why #Capturing fails. Maybe Spring creates a proxy object which delegates to another, but even then...
Anyway, unit tests like these can be more easily written:
public class UserServiceTest
{
#Tested DefaultUserService userService;
#Injectable UserRepository userRepo;
#Test
public void getUser_ShouldReturnUserObject()
{
new Expectations() {{
userRepo.findByName(anyString);
result = new User(1l, null, null, null, null);
}};
User user = userService.getUser("abc");
assertNotNull(user.getId());
assertEquals(1l, user.getId().longValue());
}
}
Unless you want to perform integration testing, it's generally best to let the mocking tool (JMockit in this case) do the injection of mock objects into tested objects.
I am very new to webapp development in general and Spring framework in particular. I am following this Spring JDBC Transactions tutorial But instead of accessing the db service from only one class, I wish to access it from multiple classes.
In the tutorial the service is defined like this
public class BookingService {
#Autowired
JdbcTemplate jdbcTemplate;
#Transactional
public void book(String... persons) {
for (String person : persons) {
System.out.println("Booking " + person + " in a seat...");
jdbcTemplate.update("insert into BOOKINGS(FIRST_NAME) values (?)", person);
}
};
public List<String> findAllBookings() {
return jdbcTemplate.query("select FIRST_NAME from BOOKINGS", new RowMapper<String>()
#Override
public String mapRow(ResultSet rs, int rowNum) throws SQLException {
return rs.getString("FIRST_NAME");
}
});
}
}
These are the Beans
#Configuration
#EnableTransactionManagement
#EnableAutoConfiguration
public class Application {
#Bean
BookingService bookingService() {
return new BookingService();
}
#Bean
DataSource dataSource() {
return new SimpleDriverDataSource() {{
setDriverClass(org.h2.Driver.class);
setUsername("sa");
setUrl("jdbc:h2:mem");
setPassword("");
}};
}
#Bean
JdbcTemplate jdbcTemplate(DataSource dataSource) {
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
System.out.println("Creating tables");
jdbcTemplate.execute("drop table BOOKINGS if exists");
jdbcTemplate.execute("create table BOOKINGS(" +
"ID serial, FIRST_NAME varchar(5) NOT NULL)");
return jdbcTemplate;
}
They instantiated the service in only the Application class and did all the transactions there
ApplicationContext ctx = SpringApplication.run(Application.class, args);
BookingService bookingService = ctx.getBean(BookingService.class);
//bookingService.doStuff()
In my test project, I copied the same Bean definitions but I performed the transactions in multiple classes.
public class foo {
ApplicationContext ctx = new AnnotationConfigApplicationContext(Application.class);
BookingService bookingService = ctx.getBean(BookingService.class);
bookingService.book(...);
// some other stuff
}
public class bar {
ApplicationContext ctx = new AnnotationConfigApplicationContext(Application.class);
BookingService bookingService = ctx.getBean(BookingService.class);
bookingService.findAllBookings(...);
// some other stuff
}
It seems that when I perform all transactions in one class only (for example, book and find in foo class) it performs as expected. But when I try to separate them into multiple classes it doesn't behave as expected. If I perform the book in foo, I cannot find in bar. What concept am I missing? Am I instantiating multiple instances of the datasource and jdbctemplate because I instantiated the service multiple times. But I thought Spring handles the injection? And since there is only one physical database then there would only be one instance of datasource and jdbctemplate. What concept did I misunderstood? Please help and point me to the right direction. Thanks.
You need to inject your dependencies, for example:
public class Foo {
#Autowired
private BookingService bookingService;
public doStuff() {
bookingService.book(...);
// some other stuff
}
}