I know there are a lot of similar issues here on stackoverflow, but none of this fixes my issue.
I want to expose some methods from my Spring Boot Repositories as Webservice, but one Repository randomly ^(1) only returns 404 on all Methods.
We are talking about following classes
#Component
public class CustomRepositoryRestConfigurer implements RepositoryRestConfigurer {
#Override
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config, CorsRegistry cors) {
config.disableDefaultExposure();
}
}
#RepositoryRestResource(exported = true)
public interface TransactionRepository extends JpaRepository<Transaction, Long> {
#Query("select t from Transaction where ....")
#RestResource(exported = true) // true is default
Page<Transaction> findAllByQuery(
// more stuff
#Param("text") String text,
Pageable pageable);
void delete(Transaction entity); // should not be exposed!
}
And following tests will fail:
#AutoConfigureMockMvc
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK, classes = Application.class)
public class SampleTest {
#Autowired
private MockMvc mvc;
#Test
public void check_profile_is_ok() throws Exception {
mvc.perform(get("/")
// fails...
.andExpect(jsonPath("$._links.transactions").value(notNullValue()))
}
#Test
public void check_access() throws Exception {
mvc.perform(get("/transactions/search/findAllByQuery")
// fails...
.andExpect(status().isOk());
}
}
When remove config.disableDefaultExposure(); the Testcase will pass, but then all Endpoints are exposed - and I don't want this.
Is there any configuration I'm missing?
I have a second repository CategoryRepository and everything is same but working.
Okay I solved it by myself. I had (falsely) a second Repository on the Transaction Entity.
Related
Problem with correct class setting for tests.
I have the following service structure
My service:
Interface
public interface ColumnsFromTableService {
List<ColumnsDto> getTableColumnsFromSource(DataProvider dataProvider, String tableName);
DataProviderSourceType myDataProviderSourceType();
#Autowired
default void regMe(ColumnsFromTableFacade columnsFromTableFacade){
columnsFromTableFacade.register(myDataProviderSourceType(),this);
}
}
Impl
#Service
#RequiredArgsConstructor
public class OracleColumnsFromTableServiceImpl implements ColumnsFromTableService {
private final DataProviderInsideDao dataProviderInsideDao;
#Override
public List<ColumnsDto> getTableColumnsFromSource(DataProvider dataProvider, String tableName) {
return dataProviderInsideDao.getColumnsByTableNameFromOracle(dataProvider, tableName);
}
#Override
public DataProviderSourceType myDataProviderSourceType() {
return DataProviderSourceType.ORACLE;
}
}
My facade:
Interface
public interface ColumnsFromTableFacade {
List<ColumnsDto> getTableColumnsFromSource(DataProvider dataProvider, String tableName);
void register(DataProviderSourceType dataProviderSourceType, ColumnsFromTableService columnsDataProviderService);
}
Impl
#Service
public class ColumnsFromTableFacadeImpl implements ColumnsFromTableFacade {
private final Map<DataProviderSourceType, ColumnsFromTableService> implementationMap = new HashMap<>();
#Override
public List<ColumnsDto> getTableColumnsFromSource(DataProvider dataProvider, String tableName) {
ColumnsFromTableService columnsFromTableService = implementationMap.get(dataProvider.getSourceType());
return columnsFromTableService.getTableColumnsFromSource(dataProvider,tableName);
}
#Override
public void register(DataProviderSourceType dataProviderSourceType, ColumnsFromTableService columnsDataProviderService) {
implementationMap.put(dataProviderSourceType, columnsDataProviderService);
}
}
For use, I inject the facade in the place I need.
Everything works in the application. When creating ColumnsFromTableService beans, Spring Boot sees the #Autowired annotation in the interface and and registers the service in the facade. But when testing this facade, I can't set it up correctly.
My test:
#ExtendWith(MockitoExtension.class)
public class EasyServiceTest {
#InjectMocks
TablesFromSourceFacadeImpl tablesFromSourceFacade;
#Test
void test(){
tablesFromSourceFacade.getAllTablesFromSource(new DataProvider());
}
}
When running the test, the facade is successfully instantiated. But the collection with implementations is empty.
enter image description here
I am using
Junit jupiter - 5.7.1
Spring boot - 2.4.3
I decided to be rough
#ExtendWith(MockitoExtension.class)
public class EasyServiceTest {
TablesFromSourceFacadeImpl tablesFromSourceFacade;
#InjectMocks
OracleTablesFromSourceServiceImpl oracleTablesFromSourceService;
#InjectMocks
OracleColumnsFromTableServiceImpl oracleColumnsFromTableService;
#BeforeEach
void setUp() {
tablesFromSourceFacade = new TablesFromSourceFacadeImpl();
tablesFromSourceFacade.register(postgresTablesFromSourceService.myDataProviderSourceType(),
postgresTablesFromSourceService);
tablesFromSourceFacade.register(oracleTablesFromSourceService.myDataProviderSourceType(),
oracleTablesFromSourceService);
}
#Test
void test(){
tablesFromSourceFacade.getAllTablesFromSource(new DataProvider());
}
}
UPDATED
The second solution to the problem is to raise either the entire context of the spring, or part of it. But in my case, this did not work, since the services are scattered across different packages, and I would have to raise the entire context, which is heavy.
I'd like to test my repository method. However when I run my test, it fails because of UnsatisfiedDependencyException. It for some reason tries to create AuthorizationServerConfig (or other bean if I remove #Configuration annotation from this one). It fails cause deeper in dependencies chain it requires RabbitMQ connection pool, that I prefer to not provide in repository test.
The question is why Spring tries to create all those beans not linked to repository logic?
I attempted to exclude all those beans with #DataMongoTest(exludeFilters: ...) and #DataMongoTest(exludeAutoConfiguration: ...) but it had no effect.
The only thing that helped was to add #Profile("!test") to all beans (all! controllers, services, components) in an application, but it smells like a very ugly solution.
Repository class is very simple:
#Repository
public interface ParticipantRepository extends MongoRepository<Participant, String> {
List<Participant> findAllByLoggedInTrueAndAccessTokenExpirationAfter(Date expirationAfter);
}
My test class:
#DataMongoTest()
#RunWith(SpringRunner.class)
public class ParticipantRepositoryTest {
#Autowired
private MongoTemplate mongoTemplate;
#Autowired
private ParticipantRepository repository;
private List<Participant> participants;
#Before
public void setUp() throws Exception {
participants = createParticipants();
repository.saveAll(participants);
}
#After
public void tearDown() throws Exception {
repository.deleteAll();
}
#Test
public void findAllByLoggedInTrueAndExpirationAfter_shouldNotReturnLoggedOutParticipants() {
List<Participant> result = repository.findAllByLoggedInTrueAndAccessTokenExpirationAfter(new Date());
getLoggedOutParticipants().forEach(participant -> assertThat(participant, not(isIn(result))));
}
...
}
I'm using Spring Boot with Solr.
I can see Spring-tx in my resolved dependencies.
When I call a #Transactional annotated method in a Spring bean,
a) at runtime, i don't see any signs of it being wrapped in any transaction management proxy, and
b) when the method throws a RuntimeException the data is not rolledback.
The email/phone repositories and just interfaces that extend
org.springframework.data.solr.repository.SolrCrudRepository
What am i missing?
At have the method annotated #Transactional in both the interface and the implementation, just in case ;-)
public interface MyServiceInterface {
#Transactional
public CreateDetailsResponse createAllDetails(
final CreateDetailsRequest createDetailsRequest) throws BusinessException;
}
public class MyService implements MyServiceInterface {
#Autowired
private EmailRepository emailRepository;
#Autowired
private EmailRepository phoneRepository;
#Transactional
public CreateDetailsResponse createAllDetails(
final CreateDetailsRequest createDetailsRequest) throws BusinessException {
//method below calls emailRepository.save(emailDocument)
saveEmail();
//method below is supposed to call phoneRepository.save(phoneDocument)
//but throws RuntimeException before save is called
savePhone();
}
//...
}
#Configuration
#EnableSolrRepositories(basePackages = "com.....repository", multicoreSupport = true,
considerNestedRepositories = true, repositoryBaseClass = SoftCommitSimpleSolrRepository.class)
#EnableAutoConfiguration
public class ConsumersServiceConfiguration {
//...
#Bean
public MyServiceInterface myService() {
return new MyService();
}
}
Supposedly Solr supports transactions:
I am trying to have my test unit up and running, and I have encountered a weird issue. My application uses an ApplicationListener class annotated as a #Component to perform an operation during startup.
During tests I have mocked the service that contains the logic, but I found that even though Mockito's when instructions work well in controller scope, the bean is not initialized for this ApplicationListener class: instead of returning what I define in the test unit, it returns either false or null - depending on the data type returned by each method in the service.
Since I have not found any way to initialize the mocked service from the test unit for the ApplicationListener class, I have decided to exclude it. To do so I have tried different approaches, being the one most often used that of creating a test application context and change its configuration. Unfortunately, nothing I have seen is working - so I am here asking for help. If possible, I would prefer not touching the ApplicationListener class and do all related coding in the test code.
I am interested in any of the two possible solutions, if they can be done:
1.- Get the mocked behaviour during the ApplicationListener execution, but I have read somewhere that this cannot be done
2.- Exclude the #Component from the test unit somehow.
TestUnit.Java:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = TestApplication.class, webEnvironment = WebEnvironment.RANDOM_PORT)
#AutoConfigureMockMvc
public class TestConfigurationService {
private MockMvc mockMvc;
#Autowired
private WebApplicationContext webApplicationContext;
#MockBean
private MockService mockService;
private void initMockBean () throws Exception {
when(mockService.isDoingSomething()).thenReturn(true);
}
#Before
public void setup() throws Exception {
// Spring mock context application setup
this.mockMvc = webAppContextSetup(webApplicationContext).build();
// Initialize ConsulService mock bean
initMockBean ();
}
}
TestApplication.java
#SpringBootApplication
#EnableAutoConfiguration
#ComponentScan(basePackages="my.base.package", excludeFilters = #Filter(type = FilterType.ASSIGNABLE_TYPE, classes = StartupConfiguration.class))
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
}
Besides what is shown in the code, I have also tried this annotation in file TestApplication.java:
#SpringBootApplication(exclude={StartupConfiguration.class})
StartupConfiguration.java
#Component
public class StartupConfiguration implements ApplicationListener<ContextRefreshedEvent> {
#Autowired
private ConfigurationService configurationService;
#Override
public void onApplicationEvent(final ContextRefreshedEvent event) {
try {
configurationService.updateConfiguration();
} catch (Exception e) {
throw new RuntimeException ("Error", e);
}
}
}
ConfigurationService.java
public interface ConfigurationService {
public void updateConfiguration () throws Exception;
}
ConfigurationServiceImpl.java
#Service
#Transactional
public class ConfigurationServiceImpl implements ConfigurationService {
#Autowired
private MService mockService;
#Override
public void updateConfiguration() throws Exception {
if (mockService.isDoingSomething()==false)
throw new Exception ("Something went wrong");
}
}
Versions:
Spring Boot 1.5.4.RELEASE,
Java 1.8
You can create mock bean of the same type and mark it with #Primary annotation to replace real bean. You can achieve this by having test such configuration:
#Configuration
#Import(TestApplication.class)
public class TestConfiguration {
#Bean
#Primary
public ConfigurationService configurationService() {
return Mockito.mock(ConfigurationService.class);
}
}
then get this mock in test:
...
public class TestConfigurationService {
...
#Autowired
ConfigurationService configurationService;
#Before
public void setUp() {
when(mockService.isDoingSomething()).thenReturn(true);
}
}
Thanks, araxn1d. Your answer gave me the clue to solve this issue.
I mocked the StartupConfiguration class in TestUnit.java:
#MockBean
private StartupConfiguration startupConfiguration;
Though in this case I was lucky: application listeners don't have returning methods, so they don't need when test configuration. If I had required that some method there returned for example true or a value, this method would not apply.
But at least for application listeners, this is enough.
I have the following unit test:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = {EqualblogApplication.class})
#WebAppConfiguration
#TestPropertySource("classpath:application-test.properties")
public class PostServiceTest {
// ...
#Test(expected = ConstraintViolationException.class)
public void testInvalidTitle() {
postService.save(new Post()); // no title
}
}
The code for save in PostService is:
public Post save(#Valid Post post) {
return postRepository.save(post);
}
The Post class is marked with #NotNull in most fields.
The problem is: no validation exception is thrown.
However, this happens only in testing. Using the application normally runs the validation and throws the exception.
Note: I would like to do it automatically (on save) and not by manually validating and then saving (since it's more realistic).
This solution works with Spring 5. It should work with Spring 4 as well. (I've tested it on Spring 5 and SpringBoot 2.0.0).
There are three things that have to be there:
in the test class, provide a bean for method validation (PostServiceTest in your example)
Like this:
#TestConfiguration
static class TestContextConfiguration {
#Bean
public MethodValidationPostProcessor bean() {
return new MethodValidationPostProcessor();
}
}
in the class that has #Valid annotations on method, you also need to annotate it with #Validated (org.springframework.validation.annotation.Validated) on the class level!
Like this:
#Validated
class PostService {
public Post save(#Valid Post post) {
return postRepository.save(post);
}
}
You have to have a Bean Validation 1.1 provider (such as Hibernate Validator 5.x) in the classpath. The actual provider will be autodetected by Spring and automatically adapted.
More details in MethodValidationPostProcessor documentation
Hope that helps
This is how I did it by loading ValidationAutoConfiguration.class into context:
#SpringBootTest
#ContextConfiguration(classes = { MyComponent.class, ValidationAutoConfiguration.class
public class MyComponentValidationTest {
#Autowired
private MyComponent myComponent;
#Test
void myValidationTest() {
String input = ...;
// static import from org.assertj.core.api.Assertions
assertThatThrownBy(() -> myComponent.myValidatedMethod(input))
.isInstanceOf(ConstraintViolationException.class)
.hasMessageContaining("my error message");
}
}
And MyComponent class:
#Component
#Validated
public class MyComponent {
public void myValidatedMethod(#Size(min = 1, max = 30) String input) {
// method body
}
)