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();
}
}
Related
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.
I'm quite a newbie to Spring boot, but here's the problem I'm facing now:
// Application.java
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Autowired
private Cluster cluster = null;
#PostConstruct
private void migrateCassandra() {
Database database = new Database(this.cluster, "foo");
MigrationTask migration = new MigrationTask(database, new MigrationRepository());
migration.migrate();
}
}
So basically, I'm trying to bootstrap a spring application, and after that, do some cassandra migrations.
I also have defined a repository for my user model:
// UserRepo.java
public interface UserRepo extends CassandraRepository<User> {
}
Now I'm trying to test my repo class using the following simple test case:
// UserRepoTest.java
#RunWith(SpringRunner.class)
#AutoConfigureTestDatabase(replace = Replace.NONE)
#DataJpaTest
public class UserRepoTest {
#Autowired
private UserRepo userRepo = null;
#Autowired
private TestEntityManager entityManager = null;
#Test
public void findOne_whenUserExists_thenReturnUser() {
String id = UUID.randomUUID().toString();
User user = new User();
user.setId(id);
this.entityManager.persist(user);
assertEquals(this.userRepo.findOne(user.getId()).getId(), id);
}
#Test
public void findOne_whenUserNotExists_thenReturnNull() {
assertNull(this.userRepo.findOne(UUID.randomUUID().toString()));
}
}
I would expect the test to pass, but instead, I got an error saying "No qualifying bean of type 'com.datastax.driver.core.Cluster' available". It looks like spring failed to autowire the cluster object, but why is that? How do I fix this? Thanks a lot!
The test environment needs to know where your beans are defined, so you have to tell it the location.
In your test class, add the #ContextConfiguration annotation:
#RunWith(SpringRunner.class)
#AutoConfigureTestDatabase(replace = Replace.NONE)
#DataJpaTest
#ContextConfiguration(classes = {YourBeans.class, MoreOfYourBeans.class})
public class UserRepoTest {
#Autowired
private UserRepo userRepo = null;
#Autowired
private TestEntityManager entityManager = null;
I'm writing unit test for an web app, and I've got problem with service layer. App is using Spring Data JPA, and H2 database for tests.
Here is my test class:
#WebAppConfiguration
#ContextConfiguration(classes = {TestConfiguration.class})
#TestPropertySource(locations = "classpath:test.properties")
#Transactional
public class AuthorCreateServiceTest extends AbstractTestNGSpringContextTests {
#Mock
private AuthorRepository authorRepository;
private AuthorCreateServiceImpl authorCreateServiceImpl;
private Author firstAuthor;
private Author secondAuthor;
#BeforeClass
public void setUp() {
authorCreateServiceImpl = new AuthorCreateServiceImpl(authorRepository);
firstAuthor = new Author();
firstAuthor.setFirstName("Leo");
firstAuthor.setLastName("Manly");
firstAuthor.setNationality("Mexico");
firstAuthor.setId(3L);
secondAuthor = new Author();
secondAuthor.setFirstName("Zorro");
secondAuthor.setLastName("Plata");
secondAuthor.setNationality("Zambia");
secondAuthor.setId(4L);
}
#Test
public void succesfullySaveAuthorTest() {
Author testAuthor = authorCreateServiceImpl.create(firstAuthor);
Assert.assertEquals(testAuthor.getFirstName(), firstAuthor.getFirstName());
}
#Test
public void failSavingAuthorTest() {
String firstName = "Man";
Author testAuthor = authorCreateServiceImpl.create(secondAuthor);
boolean isEqual = testAuthor.getFirstName().equals(firstName);
Assert.assertFalse(isEqual);
}
}
In this state testAuthor is null, but repository and createService objects exist. But if I add an Autowired annotation to the AutrhorCreateServiceImpl field, it works fine.
Is the Autowired necessary or I'm doing something wrong?
EDIT
TestConfiguration class
#ComponentScan(basePackages = {"com.altkom.library"} )
#Configuration
#TestPropertySource(locations = "classpath:test.properties")
public class TestConfiguration extends JPAConfiguration {
public TestConfiguration(Environment environment) {
super(environment);
}
#Bean(destroyMethod = "shutdown")
public DataSource dataSource() {
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
EmbeddedDatabase dataBase = builder.setType(EmbeddedDatabaseType.H2).addScript("classpath:import.sql").build();
return dataBase;
}
}
If you want to obtain bean from spring-context, this annotation is required.
In your implementation you've created a service with a mocked repository. Mocked objects return null by default. You can use Mockito.when() to override default behaviour.
I got Spring Java Config style configuration with 2 datasources:
#Configuration
#EnableTransactionManagement
public class DBConfig {
// main db
#Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("classpath:schema.sql")
.addScript("classpath:data.sql")
.build();
}
//db for test
#Bean(name = "testDataSource")
public DataSource testDataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("classpath:schema.sql")
.addScript("classpath:test-data.sql")
.build();
}
#Bean
public JdbcTemplate jdbcTemplate(){
return new JdbcTemplate(dataSource());
}
But when I autowire that datasources in my Test class and run him: i got the same result:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = DBConfig.class)
#Transactional
public class JdbcTest {
#Autowired
private JdbcTemplate jdbcTemplate;
#Autowired
#Qualifier("testDataSource")
private DataSource testDataSource;
#Test
public void findRealDb() {
String rowCount = jdbcTemplate.queryForObject("select message from messages", String.class);
System.out.println("real db " + rowCount);
}
#Test
public void findTestDb() {
String rowCount = (new JdbcTemplate(testDataSource).queryForObject("select message from messages", String.class));
System.out.println("test db " + rowCount);
}
}
So as result method findTestDb() logs same rowCount string as findRealDb() , but as you see them use different datasources to build jdbcTemplate.
The test code will be autowiring by type. I'm surprised you don't get a non-unique bean exception.
The trouble is you have two beans, one of which is qualified and one of which isn't.
You are much better off using Spring profiles and assigning the test datasource to a test profile and the production datasource to a default profile, then setting the active profile for your test case to be test.
Here's an example:
http://fahdshariff.blogspot.co.uk/2012/09/spring-3-javaconfig-unit-testing-using.html
note that you can put the #Profile annotation on an individual bean or a configuration class.
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.