Hoverfly simulationMode conflicts with Spring Cloud Config Server in unit test - java

I'm using hoverfly in my spring boot project's unit test.
The background
The spring boot project will grab its config (connection timeout etc.) from spring cloud config server.
To test whether my timeout configs work, I write a unit test, and expect the hoverfly can return with a long delay , then my customized restTemplate can throw timeout error instead of wait.
The unit test looks lilke this:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = TestApplication.class)
#FixMethodOrder(value = MethodSorters.NAME_ASCENDING)
public class CustomRestTemplateTest {
#Autowired
private RestTemplate customRestTemplate;
#ClassRule
public static HoverflyRule hoverflyRule = HoverflyRule.inSimulationMode(SimulationSource.dsl(
service("www.test.com")
.get("/")
.willReturn(success(HttpBodyConverter.json("{}")).withDelay(10, TimeUnit.SECONDS))
));
#Test
public void connectionTimeoutTest() {
customRestTemplate.getForObject("www.test.com", Object.class);
}
}
The issue
As I mentioned in section The background, when my spring boot project starts, it will grab configs from spring cloud config server, but Hoverfly captured that request and try to find the corresponding record, of course it can't , because I only defined the records for my unit test(e.g. www.test.com), so it throws error:
{"destination":"172.16.2.84:8888","error":"No match found","key":"a7ac72c9bcc3dc2b76bf0877d98f9e3a","level":"warning","method":"GET","msg":"Failed to find matching request template from template store","path":"************","query":"","time":"2017-03-08T20:55:28+08:00"}
How could I fix this? I want use hoverfly, can I set some config and exclude config server's url?

Hoverfly's developer Tommy responded me in their email list
It's a known issue: https://github.com/SpectoLabs/hoverfly-java/issues/19
Update
This has been fixed by Tommy Situ, and the code fix will be release in v0.4.3

Related

My rest services relies on an external service. How do I mock it to use on my cucumber tests?

I made some cucumber tests (given, when, then integration tests) in spring boot maven, and I was able to test my endpoints using TestRestTemplate, but only if the external service is running.
The external service job is to check some fields before I save the information to the database.
When running the application normally the external service is called by FeignClient.
Is it possible to mock the external service while using TestRestTemplate to test the endpoints?
EDIT:
Sample mockup of the structure
Controller.java
#Autowired
EmployeeSvc employeeSvc;
#PostMapping(/save)
public ResponseEntity<String> saveEmployee(#RequestBody EmployeeDTO employeeDTO) {
employeeSvc.save(employeeDTO);
}
EmployeeSvc.java
#Autowired
ExternalSvcClient externalSvcClient; //External service is called by feignclient here.
#Autowired
EmployeeRepository employeeRepository;
public void save(EmployeeDTO employeeDTO) {
externalSvcClient.check(employeeDTO);
...some other code
Employee employee = dtoTransformer.transoform(employeeDTO);
employeeRepository.save(employee);
}
OK I managed to make my tests using wiremock by following this .
For stubs be sure not to include the parameters in the path:
if your request url looks like this
myrequest/?value1=someValue
make it like this in the stubs and append the parameters on the rest template or else it won't work
myrequest/
restTemplate.getForEntity(url+"value1=someValue"....)

Data Jpa Test fails to load properties

I am using Hashi Corp vault in Spring Boot project. I am able to run the project without any issue. But when I run unit tests, secret-id and role-id are not being passed. I tried the following but got an exception saying both are empty. Tried hard coding the values, that didn't work either
EmployeeTest.java
#RunWith(SpringRunner.class)
#DataJpaTest
#ActiveProfiles(value = "ide")
#AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
public class EmployeeTest
{
private final Logger logger= LoggerFactory.getLogger(getClass());
#Autowired
EmployeeRepository employeeRepository;
#Test
public void getEmployeeById()
{
Employee employee=employeeRepository.getOne(13L);
logger.info(employee.toString());
}
}
Update:
I am able to pass secret-id and role-id through VM arguments but still properties are not resolving
Okay, it turns out when using profile from src/main/resources/application-ide.yml in spring boot test, properties are not being replaced by vault vaules. Copying the same file to src/test/resources/application-ide.yml fixes the issue.
TL;DR
For Spring Boot testing always better to use properties file from src/test/resources rather than src/main/resources

Use custom container as database with TestContainer

I'm trying to use TestContainer for an integration test of a Spring Boot application.
The database of such application resides in a custom PostgreSQL Docker image.
In the integration test the ApplicationContext is started through MockMvc and the container is started with something like
public class ITMyServiceTest {
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
#Rule
public PostgreSQLContainer testPostgres = new PostgreSQLContainer("my-custom-database-image")
.withDatabaseName("my_db")
.withUsername("my_name")
.withPassword("my_pwd");
#Test
public void shouldDoSomething() throws Exception {
this.mockMvc.perform(get("/api/do/something")).andDo(print());
}
What happens now is that the container is started, but the application context doesn't refer to it.
I can't use the JDBC URL scheme (like spring.datasource.url=jdbc:tc:postgresql://localhost/my_db) in a .properties file because:
if I specify postgresql as server it will start another server and the context will use it
if I specify the name of my container (or everything else) the test will rightly raise a java.lang.IllegalArgumentException because it is not a known database type.
How can I set the Spring' application context to refer to my container?
You can probably have different application.properties like application-it.properties and there define integration test specific configuration properties as - #TestPropertySource(locations = {"classpath:application-it.properties" } or have different profile(s) active during integration tests , like -#ActiveProfiles(AppConstants.INTEGRATIONTEST)
So I guess you would like to set your application's database port to the randomly generated database port. If that's the case then can first expose the port :
public static PostgreSQLContainer postgreSQLContainer = new PostgreSQLContainer("my-custom-database-image")
.withDatabaseName("my_db")
.withUsername("my_name")
.withPassword("my_pwd")
.withExposedPorts(5432);
Following that when you are setting the spring.datasource.url, you can set there the randomly generated port(which is mapped against the exposed port) from the postgresql container, how to set dynamic values inside application properties is shown in this blog post

How to overwrite ports defined in application.properties in integration tests after testcontainer started?

Assume I want to integration test code relying on a JPA datasource in a Spring Boot 2.x application with a PostgreSQL testcontainer (great tool for managing Docker containers from within test classes with one or few more lines of code). Assume further that I'm managing the ports (included in the JDBC URL) in application.properties, e.g.
spring.datasource.url=jdbc:postgresql://user-service-postgres:5432/user_service
In the integration test I create testcontainers with
#Rule
PostgreSQLContainer postgreSQLContainer = new PostgreSQLContainer();
In a preparation method I can access the value I want to set for spring.datasource.url with
postgreSQLContainer.getJdbcUrl()
How to tell Spring Boot in the test to use that URL instead of the one specified in application.properties.
I'd like to stick to my property files in order to minimize changes, but I'm thankful for other approaches including an explanation why they're superior or necessary as well.
I'm using Spring Boot 2.x.
Since Spring Framework 5.2.5 (Spring Boot 2.2.6) this setup is now even simpler as we can use the #DynamicPropertySource annotation and don't have to write and register a custom initializer.
Assuming you use the JUnit 5 dependency of Testcontainers, your test can look like the following:
#SpringBootTest
#Testcontainers
class ExampleIntegrationTests {
#Container
static PostgreSQLContainer postgreSQLContainer = new PostgreSQLContainer();
#DynamicPropertySource
static void dataSourceProperties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", postgreSQLContainer::getJdbcUrl);
}
}
You can read more about this new feature here. I've also covered the different application properties setup ways (depending on Spring Boot and JUnit version) in a dedicated Testcontainers guide.
You can manually override the property from within your Spring-boot test by using ContextConfiguration and ApplicationContextInitializer.
Override the property - define a static inner class:
static class Initializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
#Override
public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
TestPropertyValues
.of("spring.datasource.url=" + postgreSQLContainer.getJdbcUrl())
.applyTo(configurableApplicationContext.getEnvironment());
}
}
ApplicationContextInitializer can be used for programmatically initializing a Spring context before context refresh. Now, wire up the context initializer class by annotating at test class level with ContextConfiguration:
#ContextConfiguration(initializers = Initializer.class)
Docs:
ApplicationContextInitializer
ContextConfiguration

How do I configure Sniffy with Spring Boot?

Sniffy is a cool little project:
Sniffy counts the number of executed SQL queries and provides an API for validating them It is designed for unit tests and allows you to test if particular method doesn't make more than N SQL queries Especially it's useful to catch the ORM N+1 problem at early stages
It also provides a servlet filter which injects HTML into a page with a popup showing you executed queries. The documentation explains how to configure it for a traditional web.xml based application but not Spring Boot. I managed to register the servlet filter by adding this bean to an #Configuration class:
#Bean
public FilterRegistrationBean snifferFilter()
{
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
SnifferFilter filter = new SnifferFilter();
filter.setInjectHtml(true);
filterRegistrationBean.setFilter(filter);
filterRegistrationBean.setName("sniffer");
filterRegistrationBean.addUrlPatterns("/*");
return filterRegistrationBean;
}
I also updated the JDBC url, the documentation says:
add sniffer: prefix to the JDBC connection url For example jdbc:h2:~/test should be changed to sniffer:jdbc:h2:mem:
So I added the following to my application.yml:
spring.datasource.url: sniffer:jdbc:mysql://localhost:3306
But when I start my application it fails with this error:
URL must start with 'jdbc'
Sniffy author here!
Indeed as of version 3.0.7 (April 2016) you have to specify the driver class name explicitly in your Spring Boot application. There's an open issue in a bug tracker to configure it automatically.
By the way sniffy 3.0.5 introduced an out-of-the box support of Spring Boot using #EnableSniffy annotation, so you do not have to create the FilterRegistrationBean yourself anymore - just put the annotation to your application class like this:
import io.sniffy.boot.EnableSniffy;
#SpringBootApplication
#EnableAutoConfiguration
#EnableSniffy
public class Application {
public static void main(String[] args) throws ClassNotFoundException {
SpringApplication.run(Application.class, args);
}
}
I managed to figure out the problem, Spring Boot makes extensive use of auto configuration and was trying to detect the DatabaseDriver from the connection string. As the connection string no longer starts with jdbc it was encountering a problem.
It was simply a case of specifying the driver-class-name in my application.yml rather than letting Spring Boot trying to auto detect it:
spring.datasource.driver-class-name: io.sniffy.MockDriver

Categories

Resources