Let's say we have a service layer :
#Service
public class MyServiceImpl implements MyService {
#Autowired
private MyDAO myDAO;
#Transactional
public void myMethod(SomeObject someObject) {
myDAO.insertIntoMyDb(someObject);
}
}
Let us say myDAO uses spring jdbc :
#Repository
public class MyDAOImpl implements MyDAO {
#Autowired
private NamedParameterJdbcTemplate jdbcTemplate;
#Override
public void insertIntoMyDb(SomeObject object) {
// some code before this where we get query and param
int numberOfRowsUpdated = jdbcTemplate.update(query,param);
if(numberOfRowsUpdated != 1)
throw new Exception("Error while inserting a record in database. Actual records inserted : " + numberOfRowsUpdated);
}
}
I want to write 2 tests.
The first test will check my dao layer only. I want to make a jdbc call here , get data and verify. I don't want to mock them.
The second test is integration test. I want my service layer to call Dao layer. Note that there is transaction. Now this will give me data from DAO. Again DAO has to connect to db to get data.
I am using spring boot. My database properties are present in applicationITest.properties file.
How to do these 2 testing ? What is the correct annotations I have to use ? Can some one provide an example ?
a) Rollback will happen by default as long as you annotate your Test class with #Transactional. Documentation about Test-managed transactions.
sample test class:
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = MyTestConfig.class)
#Transactional
public class MyClassTest {
//..
#Test
public void myTest() {
//..
}
//..
}
b) Yes you can enforce the commit using the #Commit (added in Spring 4.2) test annotation. Here's the documentation.
Related
I'm trying to test a Spring Boot service that depends on both a repository and another service. I'm using TestContainers to verify that the service interacts correctly with the repository. However, I need to mock the other service in order to provide the test data that will be used. The service method in question needs to be transactional.
#Service
public class MyService {
private MyRepository myRepository; // This needs to be a real repository
private OtherService otherService; // This needs to be mocked
#Autowired
public MyService(MyRepository myRepository, OtherService otherService) {...}
#Transactional
public void methodToTest() {
// Get (mock) data from otherService, and interact with (real) myRepository
}
}
I can get it working in my test by manually creating the service with its dependencies. However, the resulting object is just a POJO that has not been enhanced by Spring to be Transactional:
#SpringBootTest
#ContextConfiguration(initializers = {TestContainersConfig.class})
public class MyServiceTest {
#Autowired
MyRepository myRepository;
OtherService otherService;
MyService myService;
#BeforeEach
void setup() {
otherService = Mockito.mock(OtherService.class);
// This works, except that the object is a POJO and so #Transactional doesn't work
myService = new MyService(myRepository, otherService);
}
#Test
#Transactional
void testMethodToTest() {
// Provide mock data
when(otherSerivce.something()).thenReturn(...);
// Run the test
myService.methodToTest();
// Would like to remove these lines: myService should already do it
TestTransaction.flagForCommit();
TestTransaction.end();
// assertions...
}
}
How can I get Spring to enhance the test MyService instance so that methodToTest will handle the transaction?
I am currently writing tests for my Android application using Hilt. The App got a Room database, which I want to use in my test classes. Furthermore, I want in some test classes an empty database and in others a database with test values. My question is if I can define in my Test, which provider (empty/full DB) is used for all injections that are happening in the test class?
I tried using the #Named annotation, Qualifiers, and #BindValue. With the first two I am able to use the correct provider for my database, but I do not know how this could be used when injecting my repository. With #BindValue I am not sure if this can be used for this at all.
Here is some simplified code of my problem:
TestDatabaseModule:
#Module
#TestInstallIn(components = SingletonComponent.class, replaces = DatabaseModule.class)
public class TestDatabaseModule {
// Provide empty DB
#Singleton
#Provides
AppDatabase provideInMemoryDbEmpty(#ApplicationContext Context context) {
return Room.inMemoryDatabaseBuilder(context, AppDatabase.class).build();
}
// Provide populated DB
#Singleton
#Provides
AppDatabase provideInMemoryDbFull(#ApplicationContext Context context, Provider<AppDatabase> databaseProvider) {
// AppDatabasePopulateCallback adds some test data
return Room.inMemoryDatabaseBuilder(context, AppDatabase.class).addCallback(new AppDatabasePopulateCallback(databaseProvider)).build();
}
}
Module providing DAO and repository
#InstallIn(SingletonComponent.class)
#Module
public class AccessModule {
#Singleton
#Provides
MyDao provideMyDao(AppDatabase appDatabase) {
return appDatabase.myDao();
}
#Provides
MyRepository providesMyRepository(MyDao myDao) {
return new MyRepository(myDao);
}
}
Test Class one:
#HiltAndroidTest
#SmallTest
public class TestOne {
#Rule
public HiltAndroidRule hiltRule = new HiltAndroidRule(this);
#Rule
public InstantTaskExecutorRule instantTaskExecutorRule = new InstantTaskExecutorRule();
// Inject repository that uses empty DB
#Inject
MyRepository myRepository;
#Before
public void createDb() {
hiltRule.inject();
}
#Test
public void testOne() {
// Use my repository with the empty database
}
Test class two:
#HiltAndroidTest
#SmallTest
public class TestTwo {
#Rule
public HiltAndroidRule hiltRule = new HiltAndroidRule(this);
#Rule
public InstantTaskExecutorRule instantTaskExecutorRule = new InstantTaskExecutorRule();
// Inject repository that uses populated DB
#Inject
MyRepository myRepository;
#Before
public void createDb() {
hiltRule.inject();
}
#Test
public void testTwo() {
// Use my repository with the full database
}
So in the TestOne class, I want to use the repository that can access the empty database, and in the TestTwo class the repository that uses the database with test data.
I know I could populate the database in every test I want to use the data. That is what I will do if it is not possible with Hilt. I am glad for any advice to solve my problem or someone answering that I am on the wrong track if it is not possible.
Thank you!
It is my repository class implementation, Im using querydsl-sql for the DB persistence, It works very good when run the spring-boot project, but i need to strcuture unit test for this fragment of code, I try to use Mocks but i dont now how use this for SQLQueryFactory class or use other types of tools for unit test
#Repository
#Transactional
public class ContactRepository implements IContactRepository {
#Inject
SQLQueryFactory queryFactory;
#Transactional(readOnly = true)
#Override
public Tuple getContactInformationQuery(String memberCode) {
return queryFactory
.select(
sspcotdatconper.direccionprin,
sspcotdatconper.numtelcelu)
.from(sspcotdatconper)
.innerJoin(sspcotperson)
.on(sspcotperson.codigoper.eq(sspcotdatconper.codigoper)
.and(sspcotdatconper.codigotipocontac.eq("GTH"))
.and(sspcotdatconper.est.eq("ACT")))
.innerJoin(sspcotfun)
.on(sspcotfun.codigoper.eq(sspcotper.codigoper))
.where(sspcotfun.codigofun.eq(memberCode))
.fetchOne();
}
}
I am working on creating integration test for a service class i am testing and I needed to mock the dao for one of the test methods. the problem is when i run the tests together some of my tests fail but when i run them individually the tests past. If i remove the mockito part all my tests pass when i run them all at once. any insight on this is appreciated
below is my code:
// here is my Service class
public class Service {
Dao dao;
public Dao getDao() {
return dao;
}
public void setDao(Dao dao) {
this.dao = dao;
}
}
//here is my integ test
#Category(IntegrationTest.class)
#RunWith(SpringRunner.class)
public class Test{
#Rule
public ExpectedException thrown = ExpectedException.none();
#Autowired
#Qualifier(Service.SERVICE_NAME)
protected Service service;
#Before
public void setUp() {
assertNotNull(service);
}
#Test
public void testDoSomethingOne() throws Exception {
Dao dao = Mockito(Dao.class)
service.setDao(dao)
boolean flag = service.doSomething();
Assert.assertTrue(flag);
}
#Test
public void testDoSomethingTwo() throws Exception {
Integer num = service.doSomething();
Assert.assertNotNull(num);
}
The test method testDoSomethingOne() sets the mock dao for the service instance which it retains for rest of the tests.
Annotate the method testDoSomethingOne() with #DirtiesContext to get a fresh context associated with the subsequent test method.
Test annotation which indicates that the ApplicationContext associated
with a test is dirty and should therefore be closed and removed from
the context cache.
Use this annotation if a test has modified the
context — for example, by modifying the state of a singleton bean,
modifying the state of an embedded database, etc. Subsequent tests
that request the same context will be supplied a new context.
You can get the dao before each test and assign it back to service after the test
something like this:
private static Dao dao;
#Before
public void setUp() {
if(dao == null) {
dao = service.getDao();
}
}
#After
public void tearDown() {
service.setDao(dao);
}
If it is a integration test you should not mock your daos, the recommended way is to use a in memory database like H2. The spring folks already provide the annotation #DataJpaTest that creates the database for you.
You can use the #DataJpaTest annotation to test JPA applications. By default, it scans for #Entity classes and configures Spring Data JPA repositories. If an embedded database is available on the classpath, it configures one as well. Regular #Component beans are not loaded into the ApplicationContext.
https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-testing-spring-boot-applications-testing-autoconfigured-jpa-test
I'm using Spring annotation based configuration in my Play application.
Controllers and DAOs are Spring beans. Controller and DAO layers are defined with different Spring profiles and each layer could be disabled separately.
I'd like to test controller layer in isolation from DAO layer. I've disabled DAO profile and redefined each of DAO beans as a Mockito mock. From functional point of view it works fine, the only thing I don't like is defining mocks manually like this:
#Configuration
#Import(AppContext.class)
public class TestAppContext {
#Bean
public DaoA getDaoA(){
return mock(DaoA.class);
}
//... all dependencies are re-defined manually
}
Is there a way to define package (like with #ComponentScan annotation)
and get all beans in that package as mocks instead of real objects?
UPD:
I'm running tests with FakeApplication (https://www.playframework.com/documentation/2.0/api/java/play/test/FakeApplication.html), so context is started not in the test level, but inside fake application startup.
public class ControllerTest extends WithApplication {
#Before
public void setUp() throws Exception {
start(fakeApplication(new GlobalSettings(){
private ApplicationContext appContext;
public void onStart(Application app) {
appContext = new AnnotationConfigApplicationContext(TestAppContext.class);
}
#Override
public <A> A getControllerInstance(Class<A> clazz) throws Exception {
return appContext.getBean(clazz);
}
}));
}
...
}
I did it like this because I wan't to make the test more reliable and test how controller works in real environment:
#Test
public void testControllerMethod() {
Result result = route(fakeRequest(GET, "/controller/method"));
assertThat(result).is(...);
}
If the number of dependencies you need to mock is huge, you can also use spring-auto-mock.
#ContextConfiguration(classes = { AutoMockRegistryPostProcessor.class, RestOfClasses.class, ... })
#RunWith(SpringJUnit4ClassRunner.class)
public class YourTest {
...
}
As you are creating the ApplicationContext on your own, you can register the postprocessor programmatically:
public void onStart(Application app) {
appContext = new AnnotationConfigApplicationContext(TestAppContext.class);
appContext.getBeanFactory().addBeanPostProcessor(new AutoMockRegistryPostProcessor())
}
Mark your unit-test with #RunWith(SpringJUnit4ClassRunner.class)
Mark your tested class as #InjectMock
Mark you Dao class as #Mock
Make use of Mockito in your project