Embedded Consul provides easy way to run Consul in integration tests.
Built on Consul 1.11.2
Compatible with jdk1.8+.
Working on all operating systems: Mac, Linux, Windows.
<dependency>
<groupId>com.pszymczyk.consul</groupId>
<artifactId>embedded-consul</artifactId>
<version>2.3.1</version>
<scope>test</scope>
</dependency>
testImplementation 'com.pszymczyk.consul:embedded-consul:2.3.1'
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringBootIntegrationTest {
@BeforeClass
public static void setup() {
consul = ConsulStarterBuilder.consulStarter().buildAndStart();
System.setProperty("spring.cloud.consul.enabled", "true");
System.setProperty("spring.cloud.consul.host", "localhost");
System.setProperty("spring.cloud.consul.port", String.valueOf(consul.getHttpPort()));
}
@Test
public void doSomethingWithSpring(){
//use your spring beans
}
}
If JUnit is on classpath, simplest way to use embedded-consul
is via
JUnit rules.
public class IntegrationTest {
@ClassRule
public static final ConsulResource consul = new ConsulResource();
private OkHttpClient client = new OkHttpClient();
@Test
public void shouldStartConsul() throws Throwable {
await().atMost(30, TimeUnit.SECONDS).until(() -> {
Request request = new Request.Builder()
.url("http://localhost:" + consul.getHttpPort() + "/v1/agent/self")
.build();
return client.newCall(request).execute().code() == 200;
});
}
}
There is also an extension for JUnit 5.
Unlike in Junit 4 with @Rule
and @ClassRule
there is no option to control the scope of ConsulProcess
.
ConsulExtension
starts Consul for a whole class just like @ClassRule
, but it resets the Consul state between tests.
class IntegrationTest {
@RegisterExtension
ConsulExtension consul = new ConsulExtension();
private OkHttpClient client = new OkHttpClient();
@Test
void shouldStartConsul() throws Throwable {
await().atMost(30, TimeUnit.SECONDS).until(() -> {
Request request = new Request.Builder()
.url("http://localhost:" + consul.getHttpPort() + "/v1/agent/self")
.build();
return client.newCall(request).execute().code() == 200;
});
}
}
The extension can also be registered via @ExtendWith
annotation and provided to test as a method argument.
@ExtendWith(ConsulExtension.class)
class IntegrationTest {
@Test
void test(ConsulProcess consul) {
OkHttpClient client = new OkHttpClient();
await().atMost(30, TimeUnit.SECONDS).until(() -> {
Request request = new Request.Builder()
.url("http://localhost:" + consul.getHttpPort() + "/v1/agent/self")
.build();
return client.newCall(request).execute().code() == 200;
});
}
}
public class IntegrationTest {
private ConsulProcess consul;
@Before
public void setup() {
consul = ConsulStarterBuilder.consulStarter().buildAndStart();
}
@After
public void cleanup() throws Exception {
consul.close();
}
/* tests as in example above */
You can automatically register Services in Consul via ConsulStarterBuilder
.
ConsulStarterBuilder.consulStarter()
.withService(
new Service("a service"),
new Service("another service", "127.0.0.1", 8000));
.buildAndStart();
The ConsulProcess can be reset at any time. Reset method do few operations:
- removing all registered services
- removes all registered checks
- removes all data from kv store
- destroy all active sessions
Invoking reset
method is faster than starting new Consul process.
consulClient.setKVBinaryValue("foo", "bar".getBytes())
// do sth
consul.reset()
assert consulClient.getKVBinaryValue("foo").getValue() == null
If you want to pass custom property which is not covered by ConsulStartrBuilder
you can pass JSON configuration.
String customConfiguration =
"{" +
"\"datacenter\": \"test-dc\"," +
"\"log_level\": \"INFO\"," +
"\"node_name\": \"foobar\"" +
"}";
ConsulProcess consul = ConsulStarterBuilder.consulStarter().withCustomConfig(customConfiguration).buildAndStart();
Given JSON configuration will be saved in addition configuration file extra_config.json
and processed after base
configuration (with highest priority).
An environment variable can be set to change the consul CDN.
# default
export CONSUL_BINARY_CDN=https://releases.hashicorp.com/consul/
Proxy can be used if necessary.
https.proxyHost=localhost
https.proxyPort=3128
├─$temp-directory
│
├── embedded-consul-$consul-version
│ ├── consul
│ └── consul.zip
├── embedded-consul-data-dir + randomNumber
│ ├── raft
│ │ ├── peers.json
│ │ ├── raft.db
│ │ └── snapshots
│ └── serf
│ ├── local.snapshot
│ └── remote.snapshot
├── embedded-consul-config-dir + randomNumber
│ ├── basic_config.json
│ └── extra_config.json
To avoid unnecessary downloads Consul binary is downloaded into static named directory /$tmp/embedded-consul-$consul-version
.
Another stuff (ports config, raft, serf) is created in dynamically named temp directories.
At the moment files are not deleted!.
Embedded Consul overrides all default ports used by Consul.
Ports are randomized so it's possible to run multiple Consul Agents in single machine.
Configuration file is stored in /$tmp/embedded-consul-config-dir$randomNumber/basic_config.json
, sample content:
{
"ports": {
"dns": 50084,
"serf_lan": 50085,
"serf_wan": 50086,
"server": 50087
},
"disable_update_check": true,
"performance": {
"raft_multiplier": 1
}
}
Support for older Java versions has been dropped throughout release 2.0.0
.
If you'd like to use embedded-consul
with JDK 1.7 the 1.1.1
is the last appropriate version.