I made a test case to test my Rest Web Service. but in test case I am seeing that the request is going to default port of jersey test framework which is http://localhost:9998 whereas my service is register on http://localhost:8080. I am unable to find that how can I change its port to 8080
public class UMServiceTest extends JerseyTest {
#Override
public Application configure() {
enable(TestProperties.LOG_TRAFFIC);
enable(TestProperties.DUMP_ENTITY);
return new ResourceConfig(UMService.class);
}
#Test
public void testFetchAll() {
System.out.println(getBaseUri()+"==========");
Response output = target("usermanagement").path("um").path("user").request().get();
assertEquals("should return status 200", 200, output.getStatus());
//assertNotNull("Should return list", output.getEntity());
}
you can give a command line arguments when you run the test such as,
Maven
mvn yourpack.UMServiceTest -Djersey.config.test.container.port=8080
or in eclipse you can pass this in run config 'Arguments' tab
You could change the Systemproperty from TestProperties for the JerseyTest too.
public class UMServiceTest extends JerseyTest
{
static
{
System.setProperty("jersey.config.test.container.port", "0");
}
...
In addition to kuhajeyan's answer here's maven config for JerseyTest port:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
....
<systemProperties>
<property>
<name>jersey.config.test.container.port</name>
<value>4410</value>
</property>
</systemProperties>
</configuration>
</plugin>
As of Jersey 2.33, the configure method can be set as in the following example taken from the Jersey Docs
#Override
protected Application configure() {
// Find first available port.
forceSet(TestProperties.CONTAINER_PORT, "0");
return new ResourceConfig(Resource.class);
}
In case, the configureDeployment is used to specify the resource, the below approach can be used.
#BeforeClass
public static void beforeClass() {
System.setProperty(
// Use a random available port
"jersey.config.test.container.port", String.valueOf(SocketUtils.findAvailableTcpPort()));
}
#AfterClass
public static void afterClass() {
System.clearProperty("jersey.config.test.container.port");
}
#Override
protected DeploymentContext configureDeployment() {
return ServletDeploymentContext.forServlet(
new ServletContainer(ResourceConfig.forApplicationClass(TestApplication.class)))
.contextPath("/cas/api")
.build();
}
Source: https://eclipse-ee4j.github.io/jersey.github.io/documentation/latest/test-framework.html#parallel
Related
We have some APIs we use in our application that are not accessible from local developer machines due to firewalls.
I want to use mockServer to mock some of these API so we can develop locally.
When running tests mockServer can be started and stopped using the maven build phases process-test-classes and verify respectively.
How can I get it to run when I start the application with mvn spring-boot:run ?
It's possible to override beans on springboot.
So you can use your beans and switch for mock values as you need
The example bellow is overriding services and using mock as you prefer but you can use interfaces as well.
Creating a service
#Service
public class ServiceReal {
#Autowired(required = false) // must be required=false. May be disabled by using mock configuration
private JdbcTemplate jdbcTemplate;
public String getInfo() {
return jdbcTemplate...// get a real value from database
}
}
Creating a mock service
#Service
#Primary
#Profile("mocklocal")
public class ServiceMock extend ServiceReal {
#Override
public String getInfo() {
return "Mocked value"
}
}
Config beans to choose one of them on properties later
#Profile("mocklocal")
#PropertySource("classpath:application-mocklocal.properties")
#Configuration
public class ConfigMock {
private static final String PROP_VALUE_TRUE = "true";
private static final boolean PROP_FALSE_DEFAULT_MISSING = false;
private static final String PROP_SERVICE_REAL = "mocklocal.service.real";
private static final String PROP_SERVICE2_REAL = "mocklocal.service2.real";
#Bean
#ConditionalOnProperty( value = PROP_SERVICE_REAL, havingValue = PROP_VALUE_TRUE, matchIfMissing = PROP_FALSE_DEFAULT_MISSING)
public ServiceReal serviceReal(){
return new ServiceMock();
}
#Bean
#ConditionalOnProperty( value = PROP_SERVICE2_REAL, havingValue = PROP_VALUE_TRUE, matchIfMissing = PROP_FALSE_DEFAULT_MISSING)
public Service2Real service2Real(){
return new Service2Mock();
}
}
Config your application-mocklocal.properties to use mock
# using ConfigMock
spring.profiles.active=mocklocal
# settig spring to override service and use mock
spring.main.allow-bean-definition-overriding=true
# disable some configuration not required in mocks. you can adjust for amqp, database or other configuration
spring.autoconfigure.exclude[0]=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
spring.autoconfigure.exclude[1]=org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration
spring.autoconfigure.exclude[2]=org.springframework.boot.autoconfigure.orm.jpa.DataSourceTransactionManagerAutoConfiguration
# enable your service to use mocks not real services
mocklocal.service.real=true
mocklocal.service2.real=true
so if you start your app using --spring.profiles.active=mocklocal you will got mock values
And you can use on tests as well
#ExtendWith(SpringExtension.class)
#AutoConfigureMockMvc
#SpringBootTest
#TestPropertySource(locations = "classpath:application-mocklocal.properties")
public class RunIntegrationTests {
#Autowired
private MockMvc mockMvc;
#Test
public void run() throws Exception{
...
}
}
When running tests mockServer can be started and stopped using the maven build phases process-test-classes and verify respectively.
So there must be some (pom) configuration like:
<plugin>
<groupId>org.mock-server</groupId>
<artifactId>mockserver-maven-plugin</artifactId>
<version>3.10.8</version>
<configuration>
<serverPort>1080</serverPort>
<proxyPort>1090</proxyPort>
<logLevel>DEBUG</logLevel>
<initializationClass>org.mockserver.maven.ExampleInitializationClass</initializationClass>
</configuration>
<executions>
<execution>
<id>process-test-classes</id>
<phase>process-test-classes</phase>
<goals>
<goal>start</goal>
</goals>
</execution>
<execution>
<id>verify</id>
<phase>verify</phase>
<goals>
<goal>stop</goal>
</goals>
</execution>
</executions>
</plugin>
This would start a mock server at process-test-classes (so before test phase) and stop it at validate (so after (post-)integration-test phase).
(link1, link2)
How can I get it to run when I start the application with mvn spring-boot:run ?
To run it with mvn spring-boot:run:
Just run mvn mockserver:start spring-boot:run! (pack it into a script/IDE launch..) (recommended)
Implement custom plugin, which commbines spring-boot-maven and mockserver-maven-plugin... (and then run mvn com.example:custom-plugin:run)
.
I had created a MockServer for my team once, for quite a similar purpose here (fortunately a short demo is also available). You can set up this server independently (say on a localhost) and add the request (url and payloads) with the corresponding response json you want to this server.
The one time change you need to do inside your project will be to route all your API request to this Mockserver during development/testing, which can be done by changing the base url of all the APIs you will be using and setting up the mockserver with appropriate json request and response. It can be done as simple as this:
public class BaseUrlLoader {
public static String NEWSRIVER_BASE_URL;
public static String FACEBOOK_BASE_URL;
public static String TWITTER_BASE_URL;
private static final String MOCKSERVER_BASE_URL = "mocksrvr.herokuapp.com/TEAM-SECRET-KEY";
public static void load(){
Properties properties= new Properties();
String activeProfile;
try{
properties.load(ClassLoader.getSystemResourceAsStream("application.properties"));
} catch (IOException e) {
System.out.println("Not able to load the application.properties file");
return;
}
activeProfile = properties.getProperty("spring.profiles.active");
System.out.println("Using "+activeProfile);
if(activeProfile.equals("Test")){
NEWSRIVER_BASE_URL=MOCKSERVER_BASE_URL;
FACEBOOK_BASE_URL= MOCKSERVER_BASE_URL;
TWITTER_BASE_URL= MOCKSERVER_BASE_URL;
}else{
NEWSRIVER_BASE_URL="api.newsriver.io";
FACEBOOK_BASE_URL="api.facebook.com";
TWITTER_BASE_URL="api.twitter.com";
}
System.out.println(NEWSRIVER_BASE_URL);
}
}
// Example- Use APIs as
public class NewsFetch {
...
public NewsFetch(){ BaseUrlLoader.load(); }
private URI buildURL(APIQuery apiQuery) throws URISyntaxException {
String mainURL = BaseUrlLoader.NEWSRIVER_BASE_URL+"v2/search";
URIBuilder url = new URIBuilder(mainURL);
url.addParameter("query", apiQuery.getLuceneQuery());
url.addParameter("soryBy", apiQuery.getSortBy());
url.addParameter("sortOrder", apiQuery.getSortOrder());
url.addParameter("limit", apiQuery.getLimit());
return url.build();
}
public HttpResponse <String> fetch(APIQuery apiQuery) throws URISyntaxException, IOException, InterruptedException {
URI uri = buildURL(apiQuery);
HttpRequest request = HttpRequest.newBuilder()
.GET()
.header("Authorization", KEY)
.uri(uri)
.build();
...
}
}
// and add the request like http://mocksrvr.herokuapp.com/TEAM-SECRET-KEY/v2/search/... to the Mockserver with the response you want.
The baseurl will change according to the current active profile. This mockserver is simple and can even be integrated with the Slackbot. See more in the readme file. There can be many bugs in the project and contributions will be appreciated.
I'm using dbunit with an h2 in-memory database to test the methods in a DAO class that I wrote. All tests in the suite used to pass successfully, but then I reorganized the directory structure of the project and now I'm getting a NoSuchTableException when I run the test suite. I feel that it's got to be some kind of build path error, but I've been banging my head over it for two days now and can't fix it.
Here's an excerpt from the test class containing some of the set-up methods:
#RunWith(PowerMockRunner.class)
#PrepareForTest(DAOUtilities.class)
public class FeedingScheduleDaoImplDBUnitTest extends DataSourceBasedDBTestCase {
private Connection connection;
private FeedingScheduleDaoImpl fsdi = new FeedingScheduleDaoImpl();
#Override
protected DataSource getDataSource() {
JdbcDataSource dataSource = new JdbcDataSource();
dataSource.setUrl("jdbc:h2:mem:default;DB_CLOSE_DELAY=-1;init=runscript from 'classpath:schema.sql'");
dataSource.setUser("sa");
dataSource.setPassword("sa");
return dataSource;
}
#Override
protected IDataSet getDataSet() throws Exception {
InputStream inputStream = getClass().getClassLoader().getResourceAsStream("data.xml");
return new FlatXmlDataSetBuilder().build(inputStream);
}
#Override
protected DatabaseOperation getSetUpOperation() {
return DatabaseOperation.REFRESH;
}
#Override
protected DatabaseOperation getTearDownOperation() {
return DatabaseOperation.DELETE_ALL;
}
#Before
public void setUp() throws Exception {
super.setUp();
connection = getConnection().getConnection();
}
#After
public void tearDown() throws Exception {
super.tearDown();
}
#Test
public void givenDataSetEmptySchema_whenDataSetCreated_thenTablesAreEqual() throws Exception {
IDataSet expectedDataSet = getDataSet();
ITable expectedTable = expectedDataSet.getTable("animals");
IDataSet databaseDataSet = getConnection().createDataSet();
ITable actualTable = databaseDataSet.getTable("animals");
Assertion.assertEquals(expectedTable, actualTable);
}
}
It's the return new FlatXmlDataSetBuilder().build(inputStream); that is actually generating the exception, but I suspect that's because the schema isn't being properly loaded in getDataSource() method.
Below is my new directory structure after the reorganization. The sql schema file and the xml data files needed for the tests are all in the src/test/resources directory.
eZoo directory structure
And here's the build section of my pom.xml. I haven't specified any source or resource directories for the build, and so if I'm understanding things correctly, that should land all of the resource files in the default target/test-classes directory. I've checked and they are in fact there.
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.3</version>
</plugin>
</plugins>
</build>
Any tips or help would be much appreciated. I'm still learning how to use Maven, and I did this reorganization in an effort to better understand the standard Maven project structure, so I'm hoping to both fix what I broke and learn how to use Maven better.
After tinkering, I found a solution to my problem. I removed the following line from my xml files:
<!DOCTYPE xml>
Here's a link to another question and answer where I found the solution: https://stackoverflow.com/a/5915063.
I am trying to migrate to springdoc-openapi; Everything runs great except being able to run unit-tests from mvn; The following maven command results in a 404:
mvn -Dtest=SwaggerUITest test -f TestSwaggerUi -P integration
Intellij has no problem running it, so I suspect that it is a classloading issue.
I have used the code from the example below and just added my unit-test
https://www.baeldung.com/spring-rest-openapi-documentation
Guthub code:
- https://github.com/eugenp/tutorials/tree/master/spring-boot-springdoc
For the unit-test I added to the pom.xml:
(I use rest-assured to check if the swagger-ui page exists)
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<scope>test</scope>
<version>3.0.2</version>
</dependency>
The test itself:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class SwaggerUITest extends AbstractRestAssuredSetupClass {
/**
* Test successful swagger-UI call
**/
#Test
public void getSwaggerUIPage() {
givenAnAdministratorIsAuthenticated()
.contentType(HTML)
.get("/swagger-ui/index.html?url=/v3/api-docs")
.then()
.statusCode(OK.value());
}
}
public abstract class AbstractRestAssuredSetupClass {
#Value("${request.logging.enabled:true}")
private boolean logRequests;
#Value("${response.logging.enabled:true}")
private boolean logResponses;
#Value("${local.server.port}")
private int port;
#Before
public void setUpRestAssured() {
RestAssured.baseURI = "http://localhost:" + port;
RestAssured.config = config().redirect(redirectConfig().followRedirects(false));
if (logRequests) {
RestAssured.filters(new RequestLoggingFilter());
}
if (logResponses) {
RestAssured.filters(new ResponseLoggingFilter());
}
}
protected RequestSpecification givenAnAdministratorIsAuthenticated() {
return RestAssured.given().auth().preemptive().basic("user", "user");
}
}
I found also someone that has the same problem: https://github.com/springdoc/springdoc-openapi/issues/99
Unfortunately no solution. I could also go back to springfox.
Issues like this caused me to migrate: https://github.com/springfox/springfox/issues/2932.
You can have a look, at the sample you are mentioning from baeldung.
We have added it to the demos witht a sample test of the UI. (SwaggerUnitTest). Its passing on travis-CI without any issue.
https://github.com/springdoc/springdoc-openapi-demos/tree/master/spring-boot-springdoc
You can also have a look at the tests:
https://github.com/springdoc/springdoc-openapi/blob/master/springdoc-openapi-ui/src/test/java/test/org/springdoc/ui/app1/SpringDocApp1Test.java
The issue got fixed after upgrading springdoc-openapi-ui to version 1.4.6
maybe you need to add this lines into you WebSecurityConfigs:
.antMatchers("/v2/api-docs").permitAll()
.antMatchers("/swagger-ui.html").permitAll()
.antMatchers("/swagger-resources/**").permitAll()
I have a Spring Boot application that hosts a REST API.
Depending on which files get deployed, I want to be able to have it load additional controllers from what is essentially a "plugin" JAR file.
For example, I'd love to be able to do something like this:
java -jar myapp.jar -Dplugins.directory=/opt/myapp/plugins
Is this possible?
Note: these would not be loaded on the fly; once deployed, the set of plugins will remain fixed. I want one application jar that remains the same in every deployment, and the behavior of the application will be determined by the plugins that are deployed alongside it.
it may not 100% Satisfy your demand.
I have two suggestion.
the easy one.
java -jar stackoverflow-1.0-SNAPSHOT.jar --spring.profiles.active=prod
and put different value "#Profile" on your controller.
#RestController
#Profile("prod")
public class URLOneController {
#PostMapping(value = "/url", consumes="application/json", produces="application/json")
public ResponseEntity<HttpStatus> insertClaim(#RequestBody String messageBody) {
return new ResponseEntity<>(HttpStatus.OK);
}
}
second suggestion ,dynamic load beanDefiniton.
#Configuration
#ConditionalOnProperty(name="external.controller.enable",havingValue = "true")
public class ExternalClassDefinitionProcessor implements
BeanDefinitionRegistryPostProcessor {
#Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
Class<?> aClass = null;
try {
aClass = contextClassLoader.loadClass("com.jin.learn.demo.UrlOneController");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder
.genericBeanDefinition(aClass);
beanDefinitionBuilder.addPropertyReference("personDao", "personDao");
BeanDefinition personManagerBeanDefinition = beanDefinitionBuilder
.getRawBeanDefinition();
registry.registerBeanDefinition("UrlOneController",
personManagerBeanDefinition);
}
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory
beanFactory) throws BeansException {
}
}
package your controller into normal jar(not use spring-boot-maven-plugin )
run your app like this command line
java -Dloader.path="lib/,config/,/home/jin/Desktop/abc/target/abc-1.0-SNAPSHOT.jar" -jar stackoverflow-1.0-SNAPSHOT.jar --external.controller.enable=true
the extra contorller in abc-1.0-SNAPSHOT.jar and your main app is stackoverflow-1.0-SNAPSHOT.jar
tips:
stackoverflow-1.0-SNAPSHOT.jar should package zip format .
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<layout>ZIP</layout>
</configuration>
</plugin>
I have a multi-module project with two projects: backend and client. The backend is a normal Spring Boot Rest API, nothing special. The client module is just a Java Library using the Rest API.
The backend has packaging of "war" as the backend as it uses JSPs, too and needs to be deployed to a servlet container. The backend is still easily testable with #SpringBootTest.
Now I want to have some integration tests inside the client module using the backend module as a sandbox server.
To use all the backend classes in the client module I added
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<attachClasses>true</attachClasses>
</configuration>
</plugin>
and configured the backend as a test dependency in client with classes
In my client/src/test/java I have a helper class which starts up the backend module
#Configuration
public class SandboxServer {
#Bean
public ConfigurableApplicationContext backend() {
return
new SpringApplicationBuilder(BackendApplication.class)
.sources(SandboxServerConfig.class)
.run("spring.profiles.active=sandbox")
}
}
The profile "sandbox" is used to setup a test database etc. But I had more problems. First problem was regarding the document root, so I configured it:
public class SandboxServerConfig
implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
#Override
public void customize(TomcatServletWebServerFactory factory) {
factory.setDocumentRoot(new File("../backend/src/main/webapp"));
}
}
But it still does not work as Spring is not picking up backend/src/main/resources/application.properties
That might be correct as it is not in the root classpath of the client module.
So it does not really work. I guess it is not possible to just start up the sibling module in an Integration test.
How can I achieve to start up the sibling spring boot module for integration testing? What is the best practice for szenarios like this?
You can override the application.properties location using TestPropertySource like this:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = BlaApplication.class)
#TestPropertySource(locations="/path/to/backend/src/main/resources/application.properties")
public class ExampleApplicationTests {
}
I found a much more solid solution. In my sibling Project "frontend" I have a Component which is starting up the backend server in integration mode if and only if it is not already running.
Benefits:
The real WAR is tested
You can start the WAR before in your IDE and let the tests run fast
If you run it with maven it is started up before all tests only once
No build configuration needed (like pre-integration in maven)
process is seperated from Junit runtime so no hassle with complex setups.
Drawbacks:
You need to build the package before you can run any integration test in the frontend. But hey, you should build your package before you test it. That's what integration test is all about.
And here is my SandboxServerProcess.class.
import org.springframework.stereotype.Component;
import javax.annotation.*;
import javax.annotation.*;
import java.io.*;
import java.net.*;
import java.util.*;
#Component
#Profile("integration")
public class SandboxServerProcess {
private static final String WAR = "../backend/target/backend.war";
private final static int PORT = 8081;
private boolean startedByMe;
#PostConstruct
public void start() throws Exception {
if (isStarted()) {
return;
}
testWarExists();
packagedWar("start");
if (waitForStartup()) {
startedByMe = true;
return;
}
throw new RuntimeException("Sandbox Server not started");
}
private void testWarExists() {
File file = new File(WAR);
if (!file.exists()) {
throw new RuntimeException("WAR does not exist:" + file.getAbsolutePath());
}
}
#PreDestroy
public void stop() throws IOException {
if (startedByMe) {
packagedWar("stop");
}
}
private void packagedWar(String command) throws IOException {
ProcessBuilder builder = new ProcessBuilder();
builder.environment().put("MODE", "service");
builder.environment().put("SPRING_PROFILES_ACTIVE", "integration");
builder.environment().put("APP_NAME", "backend");
builder.environment().put("PID_FOLDER", "./");
builder.environment().put("LOG_FOLDER", "./");
List<String> commands = new ArrayList<>();
commands.add(WAR);
commands.add(command);
builder.command(commands);
builder.inheritIO();
builder.redirectErrorStream(true);
builder.start();
}
private boolean isStarted() {
try {
Socket socket = new Socket();
InetSocketAddress sa = new InetSocketAddress("localhost", PORT);
socket.connect(sa, 500);
logger.warn("SandboxServer is started");
return true;
} catch (IOException e) {
return false;
}
}
private boolean waitForStartup() throws InterruptedException {
for (int i = 1; i < 30; i++) {
if (isStarted()) {
return true;
}
logger.warn("SandboxServer not yet ready, tries: " + i);
Thread.sleep(1000);
}
return false;
}
}