I have this piece of code in a Junit, where I clearly set the port to 8888
when(clientUtils.getLinkUrl(eq(HOSTELS_MICROSERVICE.name()), eq(HOSTELS_MICROSERVICE.name()), anyMap()))
.thenReturn("http://localhost:8888/HOSTELS/HOSTELSMethods");
stubFor(com.github.tomakehurst.wiremock.client.WireMock.get("/HOSTELS/HOSTELS_LIST").willReturn(
aResponse().withStatus(200)
.withHeader("Content-Type", APPLICATION_JSON_VALUE)
.withBody(ResourceUtils.getResourceFileAsString ("__files/HOSTELS.json"))));
but when I run the test I got this error on this line:
stubFor(com.github.tomakehurst.wiremock.client.WireMock.get("/HOSTELS/HOSTELS_LIST").willReturn(..
and the error:
wiremock.org.apache.http.conn.HttpHostConnectException: Connect to localhost:8080 [localhost/127.0.0.1, localhost/0:0:0:0:0:0:0:1] failed: Connection refused: connect
For Java users
Based on the WireMock docs.
There are 3 possibilities to use WireMock in your tests :
If you are using Wiremock as JUnit 4 rule to configure the port use :
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
...
#Rule
public WireMockRule wireMockRule = new WireMockRule(wireMockConfig().port(8888));
If you are using new instance and start it from your Test class (for example #Before) :
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
...
public class Test {
WireMockServer wm;
#BeforeEach
void setUp() {
wm = new WireMockServer(options().port(8888));
wm.start();
}
#Test
void test() {
wm.stubFor(...);
}
}
With static configuration of default instance (not using new instance in your test) :
WireMock.configureFor(8888);
For Kotlin users
If you are using kotlin you can add actual wiremock instance to stubFor and verify calls like wm.stubFor() and configure the port like in option 3 of this answer.
Posting my previous comment as an answer as it seems to have helped a few people. Thanks #jmrah. :)
For Kotlin and JUnit5, this can be resolved by adding the actual WireMockServer instance to the stubFor or verify method calls.
wireMockServer.stubFor()
or
wireMockServer.verify()
After adding this, the tests should work.
Related
We are using PACT library for contract testing. Current project relies on junit5 integration. I have to extend one test to use 2 providers instead of 1:
Current version:
#Test
#PactTestFor(providerName = "request-repository", hostInterface = "0.0.0.0", port = "9991")
public void myTestMethod() throws Exception {}
I read doc: https://docs.pact.io/implementation_guides/jvm/consumer/junit5 and it seems to be not supported yet:
Unsupported#
The current implementation does not support tests with multiple providers. This will be added in a later release.
I checked the release notes and I haven't found any updates. Is there another way how to define another provider? ie something as:
#Test
#PactTestFor(providerName = "request-repository", hostInterface = "0.0.0.0", port = "9991")
#PactTestFor(providerName = "result-repository", hostInterface = "0.0.0.0", port = "9992")
public void myTestMethod() throws Exception {}
Or in this case junit5 integration is not a good choice?
My suggestion would be to write 2 separate tests, where each test has one provider mocked via Pact and the other provider stubbed out (e.g. with a standard unit test stub).
I have a moderately heavy springboot service, it takes 10-15 seconds to boot on a happy flow, and (1-2) minutes to fail on a retry/failover flow. This is ok for my business flows, and is how I expect a healthy service to behave.
I have integration tests (that run some end-to-end flows in my service), that can only test the actual integration status while the test machine (or dev machine) is connected to a specific VPN.
I want to auto skip integration tests if I'm not connected to VPN.
consider the following code
#RunWith(SpringRunner.class)
#SpringBootTest(classes = {Server.class}, // auto scans a bunch of components
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) // slow loading context
public class IntegrationTest {
#BeforeClass
public static void beforeClass() {
Assume.assumeTrue(DnsTool.vpnConnected()); // fast failing test
}
#Test
public void testIntegration() {
// some test logic
}
}
When the assumptions pass, my tests run, and all is good.
When the assumptions fail, my tests get skipped, but only after trying to load my expensive context.
How can I avoid the long running time for my test suite?
Things I tried:
Subclassing SpringJUnit4ClassRunner, and overriding isTestMethodIgnored.
Adding a TestExecutionListener, and throwing the assumption exception in beforeTestClass
These made no impression on Spring, and the context got loaded any way.
Things I didn't try:
Lazy init comes with 2.2.X next stable release of spring I think.
Lazy init potentially makes my problem go away, but I feel like there should be some easy spring-test/junit fix that I'm missing.
Thanks in advance for the help.
To me, this sounds like something that you shouldn't do in tests at all.
Tests (at least IMHO), are supposed to check the business cases and assume that the environment is set up and ready.
Maybe it worth to delegate this functionality to build tool and CI.
Example:
Define a profile in maven (or whatever build tool you use) that will run integration tests that require VPN. Define profile that will run all the rest of integration tests as well.
Activate the profile if some system property is available.
In CI tool (like Jenkins) as a part of CI even before you run maven, run the script that will check the VPN connection. Based on the results set the system properties and run maven with these properties. The required profiles will be loaded and all the tests / only tests that do not require VPN will be run.
Update
If you need to make it work from Spring (and it looks like you prefer this way),
Spring has a special annotation called #IfProfileValue
By default, it matches against system properties and if the value doesn't match the test gets ignored.
It looks something like this (and note that you can put this annotation on class as well, then it will work for all test methods in the class):
#RunWith(SpringRunner.class)
#SpringBootTest
public class MyTestClass {
#IfProfileValue(name = "os.name", values = {"Linux"})
#Test
public void testMe() {
// will run only in linux, otherwise
// won't even try to load an
// application context
....
}
}
This covers the case when you resolve the VPN connectivity externally and run the tests with a property. However, if you want to implement the VPN connectivity check in java, this annotation along not enough because it can work only with Java system properties, so in order to work with custom logic you need to implement org.springframework.test.annotation.ProfileValueSource:
public class VPNConnectivityProfileValueSource implements ProfileValueSource {
private String vpnEnabled = "true";
public VPNConnectivityProfileValueSource () {
// no spring context is available here
ClassPathResource resource = new ClassPathResource("vpn-config.properties");
if (resource.exists()) {
// read the VPN address,
//
//this.testProps = PropertiesLoaderUtils.loadProperties(resource);
// invoke your utility, check the connectivity, etc.
this.vpnEnabled = ...
}
}
#Override
public String get(String key) {
// this is important method,
if(key.equals("vpn.enabled") {
return this.vpnEnabled;
}
else return System.getProperty(key);
}
}
The last thing is to make the test aware of the ProfileValueSource:
For this there is another special annotation that you put on the test:
#ProfileValueSourceConfiguration(VPNConnectivityProfileValueSource.class)
All in all it the test can look like this:
#RunWith(SpringRunner.class)
#SpringBootTest
#ProfileValueSourceConfiguration(VPNConnectivityProfileValueSource.class)
#IfProfileValue(name = "vpn.enabled", value = "true")
public class MyTestClass {
#Test
public void testMe() {
....
}
}
All the classes/annotations I've mentioned reside in package org.springframework.test.annotation
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
I am trying to learn integration tests with Spring. So I am following this tutorial:
http://www.lucassaldanha.com/unit-and-integration-tests-in-spring-boot/
I am fase a test Class like this:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class GreetingControllerTest {
#Test
public void helloTest(){
TestRestTemplate restTemplate = new TestRestTemplate();
Hello hello = restTemplate.getForObject("http://localhost:8080/hello", Hello.class);
Assert.assertEquals(hello.getMessage(), "ola!");
}
}
But when I mvn install, I get this error:
I/O error on GET request for "http://localhost:8080/hello": Connection refused; nested exception is java.net.ConnectException: Connection refused
So... What am I doing wrong? What I need to do to make my test work?
Note: If I run mvn spring-boot:run the project works fine and I request the end point using any browser.
That's because of the following property in your test class:
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
As per spring documentation, it binds the application to a random port. So, while sending the request, there's a chance that the app won't be running on port 8080 and hence, you get connection refused error.
If you want to run the app on a particular port, you need to remove webEnvironment property and annotate your class with the following:
#IntegrationTest("server.port=8080")
Another approach is to get the port and add it into the url, below is the snippet to get the port:
#Autowired
Environment environment;
String port = environment.getProperty("local.server.port");
You can autowire the random port value to a field in the test class if you want to:
#LocalServerPort
int port;
but you can autowire the restTemplate and you should be able to use it with relative URI without the need to know the port number:
#Autowired
private TestRestTemplate restTemplate;
#Test
public void helloTest(){
Hello hello = restTemplate.getForObject("/hello", Hello.class);
Assert.assertEquals(hello.getMessage(), "ola!");
}
I have a small app that interacts with a remote android service. I would like to mock that service in unit tests. I use Robolectric and JUnit for other test cases and shadows but I could not figure how to deal with remote services.
Is it sufficient to create and start a test service using the same package with the real service and export methods using same aidl?
Since I don't have the code for that service, I assume that I can not use Robolectric's ShadowService which requires actual class to be there.
Thanks a lot.
I would use Mockito to create a Mock of the interface and then pass that instance to your code in your tests. You could also manually create an implementation of that interface in your test code and use that.
So you have to do the mocking yourself and it is important that the code you want to tests uses some form of dependency injection to aquire a reference to the aidl interface, so you can pass your own mock in your tests.
If you want to write a unit test for service then you can use Mockito for mocking service behavior.If you want to test your service on the real device then this is how you can connect with your service.
#RunWith(AndroidJUnit4.class)
public classRemoteProductServiceTest {
#Rule
public final ServiceTestRule mServiceRule = new ServiceTestRule();
#Test
public void testWithStartedService() throws TimeoutException {
mServiceRule.startService(
new Intent(InstrumentationRegistry.getTargetContext(), ProductService.class));
//do something
}
#Test
public void testWithBoundService() throws TimeoutException, RemoteException {
IBinder binder = mServiceRule.bindService(
new Intent(InstrumentationRegistry.getTargetContext(), ProductService.class));
IRemoteProductService iRemoteProductService = IRemoteProductService.Stub.asInterface(binder);
assertNotNull(iRemoteProductService);
iRemoteProductService.addProduct("tanvi", 12, 12.2f);
assertEquals(iRemoteProductService.getProduct("tanvi").getQuantity(), 12);
}
}