How to get Hibernate Envers with Spring integration testing? - spring

Is there some way to get Hibernate Envers to run immediately on the call to save or update instead of going at the end of the transaction ?
I have a #Transactional annotation on my (effectively end-to-end test). Since the code doesn't contain test methods I'm unable to put "flush" or whatever in it, (short of editing non-test code, which is not the desire).
I'm not sure how to get Envers log working on the calls. I need it to work as I'm using the Envers audit tables to check old entries at runtime, and would like to create test cases for this functionality.
I have the following code for unit testing :
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration("/src/webapp")
#EnableWebMvc
#Transactional
#TestPropertySource(locations= {"classpath:/config/common.properties"}, properties = {"..."})
#ContextConfiguration(locations = {"..."})
public class Test {
#Autowired
private WebApplicationContext wac;
#Autowired
private UserDetailsService detailsService;
private MockMvc mockMvc;
#Before
public void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac)
.build();
}
...
}

Related

PostConstruct and test

I use spring boot 3
Main spring boot class
#EnableTransactionManagement
#SpringBootApplication
#Slf4j
public class FlexApplication{
private final ApplicationParameterManager appParamManager;
public FlexApplication(ApplicationParameterManager appParamManager) {
this.appParamManager = appParamManager;
}
#PostConstruct
public void init(){
}
....
}
#Service
#Slf4j
public class ApplicationParameterManager{
....
}
Basic test
#AutoConfigureTestDatabase(replace= AutoConfigureTestDatabase.Replace.NONE)
#DataJpaTest
public class ListUserRepositoryTest {
#Autowired
private ListUserRepository repository;
#Test
public void getListUserByUserType(){
String typeUser = "CETEST";
Pageable page = Pageable.ofSize(10);
Page<ListUser> pageListUser = repository.findAllByTypeUser(typeUser, page);
assertThat(pageListUser.getContent().size() > 5 ).isTrue();
}
}
Otherwise this test, application run well
I get this error
Parameter 0 of constructor in com.acme.FlexApplication required a bean
of type 'com.acme.parameter.ApplicationParameterManager' that could
not be found.
I think it is not related to version of Spring Boot.
As you're using #DataJpaTest , your bean is not created
Spring Docs:
#DataJpaTest can be used if you want to test JPA applications. By
default it will configure an in-memory embedded database, scan for
#Entity classes and configure Spring Data JPA repositories. Regular
#Component beans will not be loaded into the ApplicationContext.
Solution would be to use #SpringBootTest instead of #DataJpaTest if your test is not really a JPA test.
Also, still using #DataJpaTest you could add #Import(ApplicationParameterManager.class) to your test class
When using #DataJpaTest, you are not creating the whole spring context as when you run the application normally but you only create the beans responsible for data access layer.
In order to run your tests properly, you need to provide a mocked bean of type ApplicationParameterManager.
The easiest way to do it is by utilizing #MockBean annotation.
So, to make your tests work, edit the test in the following way.
#AutoConfigureTestDatabase(replace= AutoConfigureTestDatabase.Replace.NONE)
#DataJpaTest
public class ListUserRepositoryTest {
#MockBean
private ApplicationParameterManager applicationParameterManager;
#Autowired
private ListUserRepository repository;
#Test
public void getListUserByUserType(){
String typeUser = "CETEST";
Pageable page = Pageable.ofSize(10);
Page<ListUser> pageListUser = repository.findAllByTypeUser(typeUser, page);
assertThat(pageListUser.getContent().size() > 5 ).isTrue();
}
}
That way, the spring context will include a mocked bean of your required dependency.
Take a look at #MockBean java doc for more information. https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/test/mock/mockito/MockBean.html
If you prefer to run the whole spring context in order to perform full integration tests, take a look at #SpringBootTest annotation.
#DataJpaTest should be used when you want to test data access layer in isolation.

JUnit 5/Spring Boot - Use Properties but Mock Dependencies

I'm trying to write unit tests for a DAO/Repository. My intent is to mock the NamedParameterJdbcTemplate object within and verify that the surrounding business logic doesn't do any bad (i.e.: handles nulls, etc...).
I'd also like to verify that the SQL is combined correctly. I can do this with doAnswer(...), but the SQL queries live in a .properties file.
Is there a way to load a .properties file for testing, without loading all of the other dependencies of this class?
What I've Tried
I've tried decorating the Test class with various permutations of :
#ExtendWith(SpringExtension.class)
#ExtendWith(MockitoExtension.class)
#ContextConfiguration(classes = WidgetRepositoryImpl.class)
#TestPropertySource(properties = "sql.properties")
However, turning these on and off always seems to have one of the following effects:
Load the NamedParameterJdbcTemplate as a bean. This could work if I specified the nearby AppConfig class in the ContextConfiguration annotation, but then I'd need to load all of its underlying dependencies, and the whole point is to mock this field.
Load the Repository and mock the NamedParameterJdbcTemplate, but not load sql.properties. The SQL statements are now null.
I want to avoid ReflectionTestUtils.setField(widgetRepository, "namedParameterJdbcTemplate", "<put the sql here>"). This works, but there are many SQL queries (the code below is very simplified). It also introduces a risk of human error (since now we're testing strings in the Test class, not the actual properties file.
The question
Is there any way to load sql.properties for this class, but not attempt to load the NamedParameterJdbcTemplate?
Unit Test
#ExtendWith(SpringExtension.class)
#TestPropertySource(properties = "sql.properties")
class WidgetRepositoryImplTest {
#Mock
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
#Autowired
private WidgetRepository widgetRepository;
#Test
public void testGetWidgetById() {
// build a mock that returns a fake widget, and stores the SQL string for other tests
doAnswer(invocation -> { ...omitted for brevity... }).when(namedParameterJdbcTemplate).query(anyString(), anyMap(), any(WidgetMapper.class));
Widget widget = widgetRepository.getWidgetById("asdf-1234");
assertNotNull(widget);
}
Repository
#PropertySource("classpath:sql.properties")
#Slf4j
#Repository
public class WidgetRepositoryImpl implements WidgetRepository {
#Value("${widget-sql.selects.select-widget-by-id}")
private String selectWidgetByIdQuery;
#Autowired private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
#Override
public Widget getWidgetById(String id) {
Map<String, String> params = new HashMap<>();
params.put("widgetId", id);
List<Widget> results = namedParameterJdbcTemplate.query(selectWidgetByIdQuery, params, new WidgetMapper());
if (results.isEmpty()) {
return null;
}
return results.get(0);
}
sql.properties
widget-sql.selects.select-widget-by-id=select * from [WIDGETS] where WIDGET_ID=:widgetId
# a few dozen additional queries in here
The solution is to use SpringExtension with the #MockBean annotation. This annotation will generate a mock implementation of the given class and provide it as a Spring bean. This means that if your class uses #Autowired, Spring will inject the mock for you:
#ExtendWith(SpringExtension.class) // Make sure to use SpringExtension
#TestPropertySource(properties = "sql.properties")
class WidgetRepositoryImplTest {
#MockBean // Replace #Mock with #MockBean
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
#Autowired
private WidgetRepository widgetRepository;
// ...
}

Write Unit test in SpringBoot Without start application

Am developing MicroServices in springBoot. Am writing unit test for Service and DAO layer. When I use #SpringBootTest it starting application on build. But It should not start application
when I run unit test. I used #RunWith(SpringRunner.class), But am unable to #Autowired class instance in junit class. How can I configure junit test class that should not start application and how to #Autowired class instance in junit class.
Use MockitoJUnitRunner for JUnit5 testing if you don't want to start complete application.
Any Service, Repository and Interface can be mocked by #Mock annotation.
#InjectMocks is used over the object of Class that needs to be tested.
Here's an example to this.
#RunWith(MockitoJUnitRunner.class)
#SpringBootTest
public class AServiceTest {
#InjectMocks
AService aService;
#Mock
ARepository aRepository;
#Mock
UserService userService;
#Before
public void setUp() {
// MockitoAnnotations.initMocks(this);
// anything needs to be done before each test.
}
#Test
public void loginTest() {
Mockito.when(aRepository.findByUsername(ArgumentMatchers.anyString())).thenReturn(Optional.empty());
String result = aService.login("test");
assertEquals("false", result);
}
With Spring Boot you can start a sliced version of your application for your tests. This will create a Spring Context that only contains a subset of your beans that are relevant e.g. only for your web layer (controllers, filters, converters, etc.): #WebMvcTest.
There is a similar annotation that can help you test your DAOs as it only populates JPA and database relevant beans (e.g. EntitiyManager, Datasource, etc.): #DataJpaTest.
If you want to autowire a bean that is not part of the Spring Test Context that gets created by the annotatiosn above, you can use a #TestConfiguration to manually add any beans you like to the test context
#WebMvcTest(PublicController.class)
class PublicControllerTest {
#Autowired
private MockMvc mockMvc;
#TestConfiguration
static class TestConfig {
#Bean
public EntityManager entityManager() {
return mock(EntityManager.class);
}
#Bean
public MeterRegistry meterRegistry() {
return new SimpleMeterRegistry();
}
}
}
Depending your test setup, if you don't want to autowire a mock but the "real thing", You could simply annotate your test class to include exactly the classes you need (plus their transitive dependencies if necessary)
For example :
#SpringJUnitConfig({ SimpleMeterRegistry.class })
or
#SpringJUnitConfig
#Import({ SimpleMeterRegistry.class })
or
#SpringJUnitConfig
#ContextConfiguration(classes = { SimpleMeterRegistry.class })
See working JUnit5 based samples in here Spring Boot Web Data JDBC allin .

How to share #MockBeans and mock methods among my tests without inheritance?

I have a base test scenario that will be used by other integration tests. This scenario includes some mock beans (#MockBean) for external integrations.
Today, I have something like this in the integration test class:
#SpringBootTest
#WebAppConfiguration
#DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS)
#RunWith(SpringRunner.class)
#FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class OrderIT {
And the fields and annotations to prepare my integration test:
private MockMvc mockMvc;
#Autowired
private WebApplicationContext wac;
#Autowired
private ObjectMapper mapper;
#MockBean
private SomeGateway someGateway;
#MockBean
private SomeRabbitMqService someRabbitMqService ;
#MockBean
private AnotherRabbitMqService anotherRabbitMqService;
#MockBean
private SomeIntegrationService someIntegrationService ;
#MockBean
private Clock clock;
#Before
public void setup() {
//some methods mocking each service above, preparing mockMvc, etc
}
This scenario is necessary for use the MockMvc and create the main feature in the system, my Order. This Order is created by calling a POST method in a Rest API, saving the order in a memory database.
Even this working well, I need to duplicate this block of code containing these #MockBean and some #Autowired in another tests, because the Order is the base scenario to add Products to the order, set an Address to deliver, etc. Each scenario has a different integration test but all of them needs an Order.
So, how to share the "MockBeans" and the methods that mocks them among my Integration Tests? I had really bad experiences using inheritance among the tests and I really would like to try a different approach.
I end up using the Spring profiles.
I created a configuration class annotated with #Profile("test") and created the mocked beans there. Like:
#Profile("test")
#Configuration
public class MyMockConfiguration {
#Bean
public SomeService someService() {
SomeService someService = mock(SomeService .class);
// mocked methods and results
return someService ;
}
And in the Test class:
#ActiveProfiles("test")
#SpringBootTest
#WebAppConfiguration
#RunWith(SpringRunner.class)
public class MyControllerIT {
If some integration test needs to override the current mock implementation on the profile, the test just needs to declare #MockBean on the class and proceed on the mock as usual.
I'm not decide yet if test is a good name, because for me makes more sense mock the configuration by "context". So, instead of use the generic name test on the profile name, I could use createOrder and have different configuration profiles, each one with a different name and different mocks: createOrder, createOrderWithoutProducts.
I believe #ContextConfiguration was created for this purpose: https://spring.io/blog/2011/06/21/spring-3-1-m2-testing-with-configuration-classes-and-profiles

How do i write Junit4 tests without Spring transactional test support?

I would like to test transaction rollbacks in my application service. As a result i do not want to use spring's AbstractTransactionalJUnit4SpringContextTests with the #Transactional annotation as that wraps my test method in a transaction.
I know spring also offers AbstractJUnit4SpringContextTests for tests without transactions. However i need to have some transactions to save data into my database and also query data for assertions after running the service under test.
How can i write a Junit 4 spring transactional test without the default transaction test management?
Here is the answer to your question ;
#ContextConfiguration(value = "/applicationContext.xml")
public class JPASpringTest extends AbstractJUnit4SpringContextTests {
#PersistenceContext(unitName="jpadenemelocal")
EntityManager entityManager;
#Autowired
protected PlatformTransactionManager transactionManager;
#Test
public void testInsertRolManyToMany() {
TransactionStatus status = transactionManager.getTransaction(null);
// your code
transactionManager.commit(status);
}
}
The spring docs should cover this in their testing chapter
What you might want is to configure your test without the default TransactionalTestExecutionListener like this
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
#TestExecutionListeners({DependencyInjectionTestExecutionListener.class,DirtiesContextTestExecutionListener.class})
public class SimpleTest {
#Test
public void testMethod() {
// execute test logic...
}
}

Resources