Wiremock Documentation states that the location of the file specified in withBodyFile should be in src/test/resources/__files. I would like to have file in src/test/resources/Testing_ABC/Testcase2/myfile.xml.
Is there any way I can achieve this ? I tried following, but it does not seem to work !
stubFor(get(urlPathEqualTo("/abc")).willReturn
(aResponse().withHeader("Content-Type",
"text/xml; charset=utf-8").withHeader
("Content-Encoding",
"gzip")
.withBodyFile
("src/test/resources/Testing_ABC/Testcase2/myfile.xml)));
However, when I put my file in src/test/resources/__files/myfile.xml and change the path accordingly, it works fine.
I am just wondering if I can make wiremock look in some other directory in resources other than __files just in order to have nice resource structure in project.
I'm using this Kotlin Config class to customize the Root Directory.
It still requires response file to be in the __files directory.
#Configuration
class Config: WireMockConfigurationCustomizer {
override fun customize(config: WireMockConfiguration?) {
config!!.withRootDirectory("customer-client/src/test/resources")
}
}
#AutoConfigureWireMock
For Java & Spock but still using __files folder
#TestConfiguration
public class WireMockConfig {
#Bean
public WireMockConfigurationCustomizer wireMockConfigurationCustomizer() {
return config -> {
config.withRootDirectory("src/integration-test/resources");
};
}
}
Then where ever you have ApplicationContext initializer :
#SpringBootTest(classes = [Application.class, WireMockConfig.class], webEnvironment = RANDOM_PORT)
#ContextConfiguration()
abstract class WireMockIntegrationSpec extends Specification {
}
Then in your test :
#AutoConfigureWireMock(port = 9089)
#Unroll
class ApplicationSpec extends WireMockIntegrationSpec {
def "Some test" () {
}
}
Your resources will be served from src/integration-test/resources/__files.
Make sure this directory structure exists
This seems to be something you need to configure when creating the rule. Try
#Rule
public final WireMockRule rule = new WireMockRule(WireMockConfiguration.wireMockConfig()
.withRootDirectory("src/test/resources/Testing_ABC"));
and then you probably can use .withBodyFile("Testcase2/myfile.xml) in test.
Related
Is there any way to add an header to all requests?
I have to add an header to all requests. In production environments the header is added by a proxy.
Adding this header manually in all tests is annoying.
Are you using FeignClient for your requests?
If you are, and if your tests define a profile named test, you can use something like this:
#Configuration
public class FeignRequestConfiguration {
#Bean
#Profile("test")
public RequestInterceptor feignRequestInterceptorTest() {
return (RequestTemplate requestTemplate) -> {
requestTemplate.header("Some-Header", "Some-Value");
};
}
}
As #pvpkiran suggested I've created the Filter
public class AddHeadersFilter implements Filter {
#Override
public Response filter(FilterableRequestSpecification requestSpec, FilterableResponseSpecification responseSpec, FilterContext ctx) {
requestSpec.header(new Header(IntegrationBaseTest.HEADER_USER_NAME, "test-user"));
return ctx.next(requestSpec, responseSpec);
}
}
Then I've added it to all tests
#BeforeClass
public static void configureRestAssured() {
RestAssured.filters(new AddHeadersFilter());
}
Seems to work.
I've also added (I hope) helpful configuration
HeaderConfig headerConfig = headerConfig()
.overwriteHeadersWithName(HEADER_USER_NAME);
RestAssured.config().headerConfig(headerConfig);
So there is a way to override the header in some tests
Use the below code . Using RequestSpecBuilder you can achieve this .
RequestSpecBuilder reqbuild=new RequestSpecBuilder();
//Adding values like path parameters
reqbuild.addPathParam("customers", "12212");
reqbuild.addHeader("Content-Type", "application/json");
requestSpecfication=reqbuild.build();
given().spec(requestSpecfication).when().get("{customers}/").then().spec(responseSpecification).log().all();
I have a springboot commandline app where one of the production commandline args is the absolute base path. For this example we will call it
"/var/batch/"
I'm setting the basepath in my production.yml file like so with a default value.
company:
basePath: ${basePath:/var/default/}
I then have an ApplicationConfig.java file that uses that base path to create a bunch of file paths like so.
#ConfigurationProperties(prefix = "company")
public class ApplicationConfig {
private String basePath;
public String getPrimaryCarePath() {
return basePath + "ADAP-2-PCProv.dat";
}
public String getPrimaryCareDetailPath() {
return basePath + "ADAP-2-" + getBatchNo() + ".det";
}
... additional files.
}
Lastly the file paths get passed into my css parser like so.
public List<T> readCsv() throws IOException {
try (BufferedReader bufferedReader = Files.newBufferedReader(Paths.get(filePath))) {
return new CsvToBeanBuilder(bufferedReader)
.withFieldAsNull(CSVReaderNullFieldIndicator.EMPTY_SEPARATORS)
.withType(subClass)
.withSeparator('\t')
.withIgnoreLeadingWhiteSpace(true)
.build().parse();
}
}
Now everything works fine in production, but we face some issues when attempting to run mutation test. It appears as if the csv parser is looking for an absolute path rather than a relative path. We have the following path in our application-test.yml file.
company:
basePath: src/test/resources/
All our test files are stored in the test resources package, so my question is how can we use a relative path to the test resources populating the ApplicationConfig.java file while still being able to use an absolute path for production? I was thinking I could override the basepath with the test setup using ClassPathResource, but was wondering if there was a better approach.
You need 2 types of configurations : one for resources and one for absolute path.
I would suggest to add a new property app.file.path.type with values resources and absolute. You can define a new interface named FileProvider.
public interface FilePathProvider(){
Path getFilePath();
}
You can define 2 different beans with #ConditionalOnProperty and set the file path strategy:
#Configuration
public class ApplicationConfig{
#Bean
#ConditionalOnProperty(
name = "app.file.path.type",
havingValue = "absolute")
public FilePathProvider absoluteFilePathProvider(ApplicationConfig applicationConfig){
return () -> Paths.get(applicationConfig.getBasePath());
}
#ConditionalOnProperty(
name = "app.file.path.type",
havingValue = "resources")
#Bean
public FilePathProvider resourceFilePathProvider(ApplicationConfig applicationConfig){
return () -> Paths.get(this.getClass().getClassLoader().getResource(applicationConfig.getBasePath()).getPath());
}
}
In development and test mode, you will have app.file.path.type=resources and in production you will have app.file.path.type=absolute.
The advantage of this approach is that you can set the property to absolute in development as well.
I have a SpringBoot multimodule application, something like that:
core
customer1 -> depends on core
customer2 -> depends on core
I want to write integration tests for both, but I don't want to duplicate my core test code. Now I have an abstract class with SpringBootTest(classes = Customer1Application.class) and a lot of test classes, mostly testing the core functionality.
#ContextConfiguration
#SpringBootTest(classes = Customer1Application.class)
#AutoConfigureMockMvc
public abstract class AbstractSpringBootTest
{
#Autowired
protected MockMvc mockMvc;
#Autowired
protected Validator validator;
...
}
I want to check if the changes in Customer2 application break something in core functionality, so I want to run these tests with #SpringBootTest(classes = Customer2Application.class) annotation.
How is it possible to configure the application class in the annotation? Is there a way to run the tests with my other application context without manually changing the annotation or duplicating all the steps?
I don't know if it will work, but I would try removing #SpringBootTest from AbstractSpringBootTest and then defining two test classes as follows:
#SpringBootTest(classes = Customer1Application.class)
class Customer1ApplicationSpringBootTest extends AbstractSpringBootTest {}
#SpringBootTest(classes = Customer2Application.class)
class Customer2ApplicationSpringBootTest extends AbstractSpringBootTest {}
EDIT:
So I dug around Spring Boot sources and came up with this solution.
Essentially to be able to use system property or property file to configure which #SpringBootApplication is supposed to be tested you need to copy the source of class org.springframework.boot.test.context.SpringBootConfigurationFinder to your own test source root and the edit method private Class<?> scanPackage(String source) to look something like this (you do not have to use Lombok of course):
private Class<?> scanPackage(String source) {
while (!source.isEmpty()) {
val components = this.scanner.findCandidateComponents(source);
val testConfig = System.getProperties();
val testConfigFile = "test-config.properties";
val applicationClassConfigKey = "main.application.class";
try {
testConfig.load(this.getClass().getResourceAsStream("/" + testConfigFile));
} catch (IOException e) {
logger.error("Error reading configuration file: {}, using default algorithm", testConfigFile);
}
if (testConfig.containsKey(applicationClassConfigKey)) {
if (!components.isEmpty() && testConfig.containsKey(applicationClassConfigKey) && testConfig.getProperty(applicationClassConfigKey) != null) {
boolean found = false;
val configClassName = testConfig.getProperty(applicationClassConfigKey);
for (BeanDefinition component: components) {
if (configClassName.equals(component.getBeanClassName())) {
found = true;
break;
}
}
Assert.state(found,
() -> "Found multiple #SpringBootConfiguration annotated classes "
+ components + ", none of which are of type " + configClassName);
return ClassUtils.resolveClassName(
configClassName, null);
}
} else {
if (!components.isEmpty()) {
Assert.state(components.size() == 1,
() -> "Found multiple #SpringBootConfiguration annotated classes "
+ components);
return ClassUtils.resolveClassName(
components.iterator().next().getBeanClassName(), null);
}
}
source = getParentPackage(source);
}
return null;
}
Check the link for the entire project.
Did you check?
#SpringBootTest(classes = {Customer1Application.class, Customer2Application.class})
My application expects to find a configuration file called MyPojo.json, loaded into MyPojo class by MyService class:
#Data // (Lombok's) getters and setters
public class MyPojo {
int foo = 42;
int bar = 1337;
}
It's not a problem if it doesn't exist: in that case, the application will create it with default values.
The path where to read/write MyPojo.json is stored in /src/main/resources/settings.properties:
the.path=cfg/MyPojo.json
which is passed to MyService through Spring's #PropertySource as follows:
#Configuration
#PropertySource("classpath:settings.properties")
public class MyService {
#Inject
Environment settings; // "src/main/resources/settings.properties"
#Bean
public MyPojo load() throws Exception {
MyPojo pojo = null;
// "cfg/MyPojo.json"
Path path = Paths.get(settings.getProperty("the.path"));
if (Files.exists(confFile)){
pojo = new ObjectMapper().readValue(path.toFile(), MyPojo.class);
} else { // JSON file is missing, I create it.
pojo = new MyPojo();
Files.createDirectory(path.getParent()); // create "cfg/"
new ObjectMapper().writeValue(path.toFile(), pojo); // create "cfg/MyPojo.json"
}
return pojo;
}
}
Since MyPojo's path is relative, when I run this from a Unit Test
#Test
public void testCanRunMockProcesses() {
try (AnnotationConfigApplicationContext ctx =
new AnnotationConfigApplicationContext(MyService.class)){
MyPojo pojo = ctx.getBean(MyPojo.class);
String foo = pojo.getFoo();
...
// do assertion
}
}
the cfg/MyPojo.json is created under the root of my project, which is definitely not what I want.
I would like MyPojo.json to be created under my target folder, eg. /build in Gradle projects, or /target in Maven projects.
To do that, I've created a secondary settings.properties under src/test/resources, containing
the.path=build/cfg/MyPojo.json
and tried to feed it to MyService in several ways, without success.
Even if called by the test case, MyService is always reading src/main/resources/settings.properties instead of src/test/resources/settings.properties.
With two log4j2.xml resources instead (src/main/resources/log4j2.xml and src/test/resources/log4j2-test.xml), it worked :/
Can I do the same with a property file injected by Spring with #PropertySource ?
You can use #TestPropertySource annotation for this.
Example:
For single property:
#TestPropertySource(properties = "property.name=value")
For property file
#TestPropertySource(
locations = "classpath:yourproperty.properties")
So, you provide path for MyPojo.json like
#TestPropertySource(properties = "path=build/cfg/MyPojo.json")
I know you can set the server.contextPath in application.properties to change the root context.
Also, I can add an additional context in the application config for Spring Boot like the following example (in Groovy) to add an "/api" to the URL mappings of the root context:
#Bean
ServletRegistrationBean dispatcherServlet() {
ServletRegistrationBean reg = new ServletRegistrationBean(new DispatcherServlet(), "/")
reg.name = "dispatcherServlet"
reg.addInitParameter("contextConfigLocation", "")
reg.addUrlMappings("/api/*")
reg.loadOnStartup = 2
reg
}
}
I am trying to have a separate base URI "/api" specifically for web service calls, that I can leverage for security, etc. However using the above approach will mean that any of my URIs, web service or not, can be reached with "/" or "/api", and provides no concrete segregation.
Is anyone aware of a better approach to set a base path for all #RestController(s) using configuration, without having to formally prefix every controller with /api/? If I am forced to manually prefix the URI for each controller, it would be possible to mistakenly omit that and bypass my security measures specific to web services.
Here is a reference in Stack Overflow to the same type of question, which was never completely answered:
Spring Boot: Configure a url prefix for RestControllers
In continuation to the currently accepted solution the github issue addresses the same.
Spring 5.1 and above you can implement WebMvcConfigurer and override configurePathMatch method like below
#Configuration
#EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
#Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.addPathPrefix("/api",
HandlerTypePredicate.forAnnotation(RestController.class));
}
}
Now all the #RestControllers will have /api as the prefix path alongside the path configured.
Official Documentation
There's a new solution to solve this kind of problem available since Spring Boot 1.4.0.RC1 (Details see https://github.com/spring-projects/spring-boot/issues/5004)
The solution of Shahin ASkari disables parts of the Auto configuration, so might cause other problems.
The following solution takes his idea and integrates it properly into spring boot. For my case I wanted all RestControllers with the base path api, but still serve static content with the root path (f.e. angular webapp)
Edit: I summed it up in a blog post with a slightly improved version see https://mhdevelopment.wordpress.com/2016/10/03/spring-restcontroller-specific-basepath/
#Configuration
public class WebConfig {
#Bean
public WebMvcRegistrationsAdapter webMvcRegistrationsHandlerMapping() {
return new WebMvcRegistrationsAdapter() {
#Override
public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
return new RequestMappingHandlerMapping() {
private final static String API_BASE_PATH = "api";
#Override
protected void registerHandlerMethod(Object handler, Method method, RequestMappingInfo mapping) {
Class<?> beanType = method.getDeclaringClass();
RestController restApiController = beanType.getAnnotation(RestController.class);
if (restApiController != null) {
PatternsRequestCondition apiPattern = new PatternsRequestCondition(API_BASE_PATH)
.combine(mapping.getPatternsCondition());
mapping = new RequestMappingInfo(mapping.getName(), apiPattern,
mapping.getMethodsCondition(), mapping.getParamsCondition(),
mapping.getHeadersCondition(), mapping.getConsumesCondition(),
mapping.getProducesCondition(), mapping.getCustomCondition());
}
super.registerHandlerMethod(handler, method, mapping);
}
};
}
};
}
}
Also You can achieve the same result by configuring WebMVC like this:
#Configuration
public class PluginConfig implements WebMvcConfigurer {
public static final String PREFIX = "/myprefix";
#Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.addPathPrefix(PREFIX, c -> c.isAnnotationPresent(MyCustomAnnotation.class));
}
}
Implement WebMvcConfigurer on any #Configuration class.
Override configurePathMatch method.
You can do many useful things with PathMatchConfigurer e.g. add prefix for several classes, that satisfy predicate conditions.
I had the same concern and was not a fan of the Spring EL option due to the issues documented and I wanted the prefix to be tightly controlled in the controllers but I did not want to depend on the developers doing the right thing.
There might be a better way these days but this is what I did. Can you guys see any downsides, I am still in the process of testing any side-effects.
Define a custom annotation.
This allows a developer to explicitly provide typed attributes such as int apiVersion(), String resourceName(). These values would be the basis of the prefix later.
Annotated rest controllers with this new annotation
Implemented a custom RequestMappingHandlerMapping
In the RequestMappingHandlerMapping, I could read the attribute of the custom annotation and modify the final RequestMappingInfo as I needed. Here are a few code snippets:
#Configuration
public class MyWebMvcConfigurationSupport extends WebMvcConfigurationSupport {
#Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
return new MyCustomRequestMappingHandlerMapping();
}
}
And in the MyCustomRequestMappingHandlerMapping, overwrite the registerHandlerMethod:
private class MyCustomRequestMappingHandlerMapping extends RequestMappingHandlerMapping {
private Logger myLogger = LoggerFactory.getLogger(MyCustomRequestMappingHandlerMapping.class);
public MyCustomRequestMappingHandlerMapping() {
super();
}
#Override
protected void registerHandlerMethod(Object handler, Method method, RequestMappingInfo mapping) {
// find the class declaring this method
Class<?> beanType = method.getDeclaringClass();
// check for the My rest controller annotation
MyRestController myRestAnnotation = beanType.getAnnotation(MyRestController.class);
if (myRestAnnotation != null) {
// this is a My annotated rest service, lets modify the URL mapping
PatternsRequestCondition oldPattern = mapping.getPatternsCondition();
// create a pattern such as /api/v${apiVersion}/${resourceName}
String urlPattern = String.format("/api/v%d/%s",
myRestAnnotation.apiVersion(),
myRestAnnotation.resourceName());
// create a new condition
PatternsRequestCondition apiPattern =
new PatternsRequestCondition(urlPattern);
// ask our condition to be the core, but import all settinsg from the old
// pattern
PatternsRequestCondition updatedFinalPattern = apiPattern.combine(oldPattern);
myLogger.info("re-writing mapping for {}, myRestAnnotation={}, original={}, final={}",
beanType, myRestAnnotation, oldPattern, updatedFinalPattern);
mapping = new RequestMappingInfo(
mapping.getName(),
updatedFinalPattern,
mapping.getMethodsCondition(),
mapping.getParamsCondition(),
mapping.getHeadersCondition(),
mapping.getConsumesCondition(),
mapping.getProducesCondition(),
mapping.getCustomCondition()
);
}
super.registerHandlerMethod(handler, method, mapping);
}
}
Slightly less verbose solution which doesn't duplicate the logic of checking the annotation, but only changes the mapping path:
private static final String API_PREFIX = "api";
#Bean
WebMvcRegistrationsAdapter restPrefixAppender() {
return new WebMvcRegistrationsAdapter() {
#Override
public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
return new RequestMappingHandlerMapping() {
#Override
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
RequestMappingInfo mappingForMethod = super.getMappingForMethod(method, handlerType);
if (mappingForMethod != null) {
return RequestMappingInfo.paths(API_PREFIX).build().combine(mappingForMethod);
} else {
return null;
}
}
};
}
};
}
Side effects
Your error controller will also be mapped under /api/error, which breaks error handling (DispatcherServlet will still redirect errors to /error without prefix!).
Possible solution is to skip /error path when adding /api prefix in the code above (one more "if").
Someone has filed an issue in the Spring MVC Jira and come up with a nice solution, which I am now using. The idea is to use the Spring Expression Language in the prefix placed in each RestController file and to refer to a single property in the Spring Boot application.properties file.
Here is the link of the issue: https://jira.spring.io/browse/SPR-13882