
io.specto.hoverfly.junit.rule.HoverflyRule Maven / Gradle / Ivy
/**
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this classpath except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* Copyright 2016-2016 SpectoLabs Ltd.
*/
package io.specto.hoverfly.junit.rule;
import io.specto.hoverfly.junit.core.*;
import io.specto.hoverfly.junit.dsl.HoverflyDsl;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.rules.ExternalResource;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Optional;
import static io.specto.hoverfly.junit.core.HoverflyConfig.configs;
import static io.specto.hoverfly.junit.core.HoverflyMode.CAPTURE;
import static io.specto.hoverfly.junit.core.HoverflyMode.SIMULATE;
import static io.specto.hoverfly.junit.core.SimulationSource.empty;
import static io.specto.hoverfly.junit.core.SimulationSource.file;
import static io.specto.hoverfly.junit.rule.HoverflyRuleUtils.*;
/**
*
The {@link HoverflyRule} auto-spins up a {@link Hoverfly} process, and tears it down at the end of your tests. It also configures the JVM
* proxy to use {@link Hoverfly}, so so long as your client respects these proxy settings you shouldn't have to configure it.
* Example Usage
*
* public class SomeTest {
* {@code @ClassRule}
* public static HoverflyRule hoverflyRule = HoverflyRule.inSimulationMode(classpath("test-service.json"))
*
* {@code @Test}
* public void test() { //All requests will be proxied through Hoverfly
* // Given
* {@code final RequestEntity bookFlightRequest = RequestEntity.delete(new URI("http://www.other-anotherService.com/api/bookings/1")).build();}
*
* // When
* {@code final ResponseEntity bookFlightResponse = restTemplate.exchange(bookFlightRequest, Void.class);}
*
* // Then
* assertThat(bookFlightResponse.getStatusCode()).isEqualTo(HttpStatus.NO_CONTENT);
* }
* }
*
* You can provide data from a Hoverfly JSON simulation, or alternatively you can use a DSL - {@link HoverflyDsl}
* It is also possible to capture data:
*
* @ClassRule
* public static HoverflyRule hoverflyRule = HoverflyRule.inCaptureMode("recorded-simulation.json");
*
* The recorded data will be saved in your src/test/resources/hoverfly directory
* It's recommended to always use the {@link ClassRule} annotation, so you can share the same instance of Hoverfly through all your tests.
* This avoids the overhead of starting Hoverfly multiple times, and also helps ensure all your system properties are set before executing any other code.
* If you want to change the data, you can do so in {@link Before} method by calling {@link HoverflyRule#simulate}, but this will not be thread safe.
*
* @see SimulationSource
* @see HoverflyDsl
*/
public class HoverflyRule extends ExternalResource {
private static final Logger LOGGER = LoggerFactory.getLogger(HoverflyRule.class);
private final Hoverfly hoverfly;
private final HoverflyMode hoverflyMode;
private Path capturePath;
private SimulationSource simulationSource;
private HoverflyRule(final SimulationSource simulationSource, final HoverflyConfig hoverflyConfig) {
this.hoverflyMode = SIMULATE;
this.hoverfly = new Hoverfly(hoverflyConfig, hoverflyMode);
this.simulationSource = simulationSource;
this.capturePath = null;
}
private HoverflyRule(final Path capturePath, final HoverflyConfig hoverflyConfig) {
this.hoverflyMode = CAPTURE;
this.hoverfly = new Hoverfly(hoverflyConfig, hoverflyMode);
this.simulationSource = null;
this.capturePath = capturePath;
}
public HoverflyRule(final HoverflyConfig hoverflyConfig) {
this.hoverflyMode = CAPTURE;
this.hoverfly = new Hoverfly(hoverflyConfig, hoverflyMode);
this.simulationSource = null;
this.capturePath = null;
}
/**
* Instantiates a rule which runs {@link Hoverfly} in capture mode if
* recorded file is not present, or in simulation mode if record file is present
*
* @param recordFile the path where captured or simulated traffic is taken. Relative to src/test/resources/hoverfly
* @return the rule
*/
public static HoverflyRule inCaptureOrSimulationMode(String recordFile) {
return inCaptureOrSimulationMode(recordFile, configs());
}
/**
* Instantiates a rule which runs {@link Hoverfly} in capture mode if
* recorded file is not present, or in simulation mode if record file is present
*
* @param recordFile the path where captured or simulated traffic is taken. Relative to src/test/resources/hoverfly
* @param hoverflyConfig the config
* @return the rule
*/
public static HoverflyRule inCaptureOrSimulationMode(String recordFile, HoverflyConfig hoverflyConfig) {
final Path path = fileRelativeToTestResourcesHoverfly(recordFile);
if (Files.exists(path) && Files.isRegularFile(path)) {
return inSimulationMode(file(path), hoverflyConfig);
} else {
return inCaptureMode(recordFile, hoverflyConfig);
}
}
/**
* Instantiates a rule which runs {@link Hoverfly} in capture mode
*
* @param outputFilename the path to the recorded name relative to src/test/resources/hoverfly
* @return the rule
*/
public static HoverflyRule inCaptureMode(String outputFilename) {
return inCaptureMode(outputFilename, configs());
}
/**
* Instantiates a rule which runs {@link Hoverfly} in capture mode
*
* @param outputFilename the path to the recorded name relative to src/test/resources/hoverfly
* @param hoverflyConfig the config
* @return the rule
*/
public static HoverflyRule inCaptureMode(String outputFilename, HoverflyConfig hoverflyConfig) {
createTestResourcesHoverflyDirectoryIfNoneExisting();
return new HoverflyRule(fileRelativeToTestResourcesHoverfly(outputFilename), hoverflyConfig);
}
/**
* Instantiates a rule which runs {@link Hoverfly} in simulate mode
*
* @param simulationSource the simulation to import
* @return the rule
*/
public static HoverflyRule inSimulationMode(final SimulationSource simulationSource) {
return inSimulationMode(simulationSource, configs());
}
public static HoverflyRule inSimulationMode(final SimulationSource simulationSource, final HoverflyConfig hoverflyConfig) {
return new HoverflyRule(simulationSource, hoverflyConfig);
}
/**
* Instantiates a rule which runs {@link Hoverfly} in simulate mode with no data
*
* @return the rule
*/
public static HoverflyRule inSimulationMode() {
return inSimulationMode(configs());
}
/**
* Instantiates a rule which runs {@link Hoverfly} in simulate mode with no data
*
* @param hoverflyConfig the config
* @return the rule
*/
public static HoverflyRule inSimulationMode(final HoverflyConfig hoverflyConfig) {
return inSimulationMode(empty(), hoverflyConfig);
}
/**
* Log warning if {@link HoverflyRule} is annotated with {@link Rule}
*/
@Override
public Statement apply(Statement base, Description description) {
if (isAnnotatedWithRule(description)) {
LOGGER.warn("It is recommended to use HoverflyRule with @ClassRule to get better performance in your tests, and prevent known issue with Apache HttpClient. For more information, please see https://github.com/SpectoLabs/hoverfly-java.");
}
return super.apply(base, description);
}
/**
* Starts in instance of Hoverfly
*/
@Override
protected void before() throws Throwable {
hoverfly.start();
importSimulationSource();
}
/**
* Stops the managed instance of Hoverfly
*/
@Override
protected void after() {
try {
if (hoverflyMode == CAPTURE) {
hoverfly.exportSimulation(capturePath);
}
} finally {
hoverfly.close();
}
}
/**
* Gets the proxy port this has run on, which could be useful when running {@link Hoverfly} on a random port.
*
* @return the proxy port
*/
public int getProxyPort() {
return hoverfly.getHoverflyConfig().getProxyPort();
}
/**
* Gets started Hoverfly mode
*
* @return the mode.
*/
public HoverflyMode getHoverflyMode() {
return hoverflyMode;
}
/**
* Changes the Simulation used by {@link Hoverfly}
*
* @param simulationSource the simulation
*/
public void simulate(SimulationSource simulationSource) {
if (simulationSource == null) {
simulationSource = empty();
}
this.simulationSource = simulationSource;
importSimulationSource();
}
private void importSimulationSource() {
if (hoverfly.getMode() == SIMULATE) {
hoverfly.importSimulation(simulationSource);
}
}
/**
* Stores what's currently been captured in the currently assigned file, wipes the simulation, then starts capture again
* ready to store in the new file once complete.
* @param recordFile the path where captured or simulated traffic is taken. Relative to src/test/resources/hoverfly
*/
public void capture(final String recordFile) {
if (hoverfly.getMode() == CAPTURE) {
if (capturePath != null) {
hoverfly.exportSimulation(capturePath);
}
hoverfly.importSimulation(empty());
capturePath = fileRelativeToTestResourcesHoverfly(recordFile);
}
}
public static HoverflyRule inCaptureMode() {
return inCaptureMode(configs());
}
public static HoverflyRule inCaptureMode(HoverflyConfig hoverflyConfig) {
return new HoverflyRule(hoverflyConfig);
}
public String getAuthHeaderName() {
return HoverflyConstants.X_HOVERFLY_AUTHORIZATION;
}
public String getAuthHeaderValue() {
Optional authToken = hoverfly.getHoverflyConfig().getAuthToken();
return authToken.map(s -> "Bearer " + s).orElse(null);
}
}