I am using TestContainer to run RabbitMQ instance in order to use it in my integration tests.
I create a Junit 5 extension class that implement the BeforeAllCallback interface to run the container only once before my tests, to connect to the container i need to retrieve the mapped port that is exposed in my host machine, so i am wandering if there is any solution in order to access the extension class field from my integration test class.
The Extension
public class RmqExtension implements BeforeAllCallback {
private static final String DEFAULT_USER = "guest";
private static final String DEFAULT_PASS = "guest";
public static final int RABBIT_HTTP_API_PORT = 15672;
private static final String RABBIT_MQ_IMAGE_NAME = "rmqImage";
private static final String RABBIT_MQ_OVERVIEW_PATH = "/api/overview";
private static final GenericContainer rabbitMqContainer = new GenericContainer(DockerImageName.parse(RABBIT_MQ_IMAGE_NAME))
.withExposedPorts(RABBIT_HTTP_API_PORT)
.waitingFor(Wait.forHttp(RABBIT_MQ_OVERVIEW_PATH).withBasicCredentials(DEFAULT_USER, DEFAULT_PASS).forStatusCode(HttpStatus.SC_OK));
#Override
public void beforeAll(ExtensionContext extensionContext) throws Exception {
rabbitMqContainer.start();
}
}
My test Class
#ExtendWith(RmqExtension.class)
class RabbitMqIT {
private int myPort;
#Test
void myTest(){
// What i need to do
myPort = rabbitMqContainer.getMappedPort(15672);
}
}
I am unsure what is the most elegant JUnit-Jupiter-idiomatic way to do this, but if there is only 1 instance of the container per JVM process, you could either use a public static field or save it System Properties.
Also, see the Singleton Container Pattern for another example of how to do this without JUnit:
https://www.testcontainers.org/test_framework_integration/manual_lifecycle_control/#singleton-containers
Related
I am using spring-data-elasticsearch v3.2.4.RELEASE which is available via spring-boot-starter-data-elasticsearch v2.2.4.RELEASE.
I want to do the integration tests for this but the available option which is this:
https://github.com/allegro/embedded-elasticsearch not working.
The part which I tried to get started as POC is below and it is throwing exception:
public class EmbeddedElasticConfiguration {
public static final String VERSION = "6.8.4";
public static final String DOWNLOAD_DIRECTORY = "<path>\\test-elasticsearch";
public static final String INSTALLATION_DIRECTORY = "<path>\test-elasticsearch";
public static final String NAME = "elasticsearch";
public static final String TRANSPORT_PORT = "9300";
public static final String HTTP_CLIENT_PORT = "9200";
public static final String TEST_INDEX = "salesorder";
public static final String TEST_TYPE = "salesorder";
public static final String RESOURCE_LOCATION = "src/test/resources/salesorder-mapping.json";
private ObjectMapper objectMapper = new ObjectMapper();
EmbeddedElastic embeddedElastic;
#Test
public void configure() throws IOException, InterruptedException {
embeddedElastic = EmbeddedElastic.builder()
.withElasticVersion(VERSION)
.withSetting(TRANSPORT_TCP_PORT, 9300)
.withSetting(CLUSTER_NAME, "my-cluster")
//.withPlugin("analysis-stempel")
.withDownloadDirectory(new File(DOWNLOAD_DIRECTORY))
.withInstallationDirectory(new File(INSTALLATION_DIRECTORY))
.withSetting(HTTP_PORT, 9200)
.withIndex(TEST_INDEX, IndexSettings.builder()
.withType(TEST_TYPE, readMappingFromJson())
.build())
.build();
embeddedElastic.start();
}
private String readMappingFromJson() throws IOException {
final File file = ResourceUtils.getFile(RESOURCE_LOCATION);
String mapping = new String(Files.readAllBytes(file.toPath()));
System.out.println("mapping: "+ mapping);
return mapping;
}
#After
public void stopServer() {
embeddedElastic.stop();
}
}
I am below getting below exception:
pl.allegro.tech.embeddedelasticsearch.EmbeddedElasticsearchStartupException: Failed to start elasticsearch within time-out
at pl.allegro.tech.embeddedelasticsearch.ElasticServer.waitForElasticToStart(ElasticServer.java:127)
at pl.allegro.tech.embeddedelasticsearch.ElasticServer.start(ElasticServer.java:50)
at pl.allegro.tech.embeddedelasticsearch.EmbeddedElastic.startElastic(EmbeddedElastic.java:82)
at pl.allegro.tech.embeddedelasticsearch.EmbeddedElastic.start(EmbeddedElastic.java:63)
at com.xxx.elasticsearch.adapter.configuration.EmbeddedElasticConfiguration.configure(EmbeddedElasticConfiguration.java:50)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
Can someone help with any other options for integration tests in elasticsearch with spring-data or How should I write integration tests for elasticsearch.
I know there are other answers on stackoverflow and other portals for embedded-elasticsearch but those are not working with my current elasticsearch version.
You did not write what version of JUnit you are using. I can tell you how we handle this in Spring Data Elasticsearch itself:
For JUnit 4 you can check the JUnit 4 Rule that uses the Utils class to set up a local running Elasticsearch node and tears it down at the end.
For JUnit 5 you might have a look at how this is handled in the current master branch,the relevant classes are found here.
By using the annotation SpringIntegrationTest a local Elasticsearch is started and automatically shut down when all tests are done. Internally there is quite some work done in setting the cluster up, getting the info into the JUnit extension and enabling Spring autowiring of the relevant information into the configuration class. This setup is quite complex, but in the end it uses the same Utils class mentioned above.
I hope this gives a good starting point
I'm facing an issue setting a system property defined in a servlet from the JUnit test. My servlet initialization looks like this:
public class MyServlet extends HttpServlet
{
private static final long serialVersionUID = 4306648703337839989L;
private static final String CLASS_NAME = MyServlet.class.getName();
public static final String PROPERTY_NAME = CLASS_NAME + ".myProperty";
private static final long DEFAULT_PROPERTY_VALUE = 120000;
private static final long PROPERTY_VALUE;
static
{
long propValue = DEFAULT_PROPERTY_VALUE;
final String propertyStr = java.lang.System.getProperty(PROPERTY_NAME);
if (propertyStr != null)
{
propValue = Long.parseLong(propertyStr);
}
PROPERTY_VALUE = propValue;
}
}
Now my JUnit test sets the property value to say 5000 as follows:
#Test
public void testMyServlet()
{
java.lang.System.setProperty(MyServlet.PROPERTY_NAME, "5000");
<test>
}
When I run the test, the value of the static final field 'PROPERTY_VALUE' is resolved to 120000 rather than 5000. So obviously the servlet init happens before the test and that's why the value of the field is already resolved to the default value before the test can set the system property. I'm looking for suggestions to get around this so that my test can set that system property to 5000.
Thanks!
EDIT: Solution
I was able to get the test working by setting the system property to 5000 in two ways:
Solution 1: Set the system property in a static block at the very top of the test class.
public class MyTest
{
static
{
java.lang.System.setProperty("com.servlets.MyServlet.myProperty", "5000");
}
#Test
public void testMyServlet()
{
<my test>
}
}
Solution 2: Set the system property in the JUnit test itself but using the property key as the full canonical name.
public class MyTest
{
#Test
public void testMyServlet()
{
java.lang.System.setProperty("com.servlets.MyServlet.myProperty", "5000");
<my test>
}
}
I was able to get the test working by setting the system property to 5000 in two ways:
Solution 1: Set the system property in a static block at the very top of the test class.
public class MyTest
{
static
{
java.lang.System.setProperty("com.servlets.MyServlet.myProperty", "5000");
}
#Test
public void testMyServlet()
{
<my test>
}
}
Solution 2: Set the system property in the JUnit test itself but using the property key as the full canonical name.
public class MyTest
{
#Test
public void testMyServlet()
{
java.lang.System.setProperty("com.servlets.MyServlet.myProperty", "5000");
<my test>
}
}
I am wondering if this is possible. I have tried a few implementations using #mock and #spy to create a new string that has the same variable name as the one in a injectMocked class however I get the following error:
Cannot mock/spy class java.lang.String
Mockito cannot mock/spy following:
- final classes
- anonymous classes
- primitive types
In the class that is being used in InjectMocked I have:
public class Service{
String url = getUrl();
...
}
However I want to use a different url for testing as we have one for a testing environment.
I have tried things such as:
#Mock
private String url = "myUrlString";
#Spy
private String url = "myUrlString";
What I want is for when I run my Test the new value for url will be injected into the inJectMock and will be used instead of the other one.
Example:
#RunWith(MockitoJUnitRunner.class)
public class ServiceTest{
#Mock // or similar
private String url = "http://....";
#InjectMocks
private Service service = new Service();
}
So when the test runs the class is like this:
public class Service{
// Uses the inject url instead of the method that it originally uses
String url = "http://....";
...
}
Is this possible? If so how and if not how come? I can't be the only person to think to do this, however I cannot find any documentation on it.
You should just set the url with your test value, like service.setUrl(testUrl);. Mockito is not intended to provide mock values for variables, but to provide mocked implementations of methods you don't want to run in your unit tests.
For example if you have a class like this:
public class UrlProvider {
public String getUrl(){
return "http://real.url";
}
}
and you use it in your service:
public class Service{
UrlProvider provider;
public Service(UrlProvider provider){
this.provider = provider;
}
...
}
then you can easily change the returned value of the getUrl method:
#RunWith(MockitoJUnitRunner.class)
public class ServiceTest{
#Mock
private UrlProvider urlProvider;
#InjectMocks
private Service service = new Service();
#Before
public void init(){
when(urlProvider.getUrl()).thenReturn("http://test.url");
}
...
}
I have an application with 3 layers:
App <--> Graph <--> Couchbase
I'm trying to test the GraphConnector by mocking the couchbase layer and "replacing" it with a very basic in-memory graph implementation, using the same approach demonstrated in the JMockit tutorial.
This is my test class (pardon the poor indentation, didn't get the hang of it yet):
public class GraphConnectorTest {
public static final class MockCouchbase extends MockUp<ICouchConnector> {
private Map<String, CouchEntry> couch;
#Mock
public void $clinit() {
couch = new HashMap<String, CouchEntry>();
}
#Mock
public void put(CouchEntry entry) {
couch.put(entry.getKey(), entry);
}
#Mock
public CouchEntry get(String key) {
return couch.get(key);
}
}
GraphConnectorImpl graph = new GraphConnectorImpl();
#BeforeClass
public static void setUpClass() {
new MockCouchbase();
}
#Test
public void testPost() throws Exception {
GraphNode node = new GraphNode(GraphNodeType.DOMAIN, "alon.com");
graph.post(node);
GraphNode retNode = graph.getSingleNode(node.getValue(), node.getType());
assertEquals(node.getValue(), retNode.getValue());
assertEquals(node.getType(), retNode.getType());
}
}
And here is my class under test:
public class GraphConnectorImpl implements IGraphConnector {
private static ICouchConnector couch = new CouchConnectorImpl(); // <-- Basic implementation which I don't want the test to execute
#Override
public void post(GraphNode node) {
CouchEntry entry = new CouchEntry(node.getValue(), JsonDocument.create(node.getValue()));
couch.put(entry);
}
#Override
public GraphNode getSingleNode(String nodeName, GraphNodeType nodeType) {
return new GraphNode(nodeType, couch.get(nodeName).getKey());
}
}
For some reason, the class MockCouchbase that I created within the test class isn't automatically bound to the private field ICouchConnector couch of the tested class, as shown in the tutorial. Instead, the real implementation is called, which is obviously undesirable.
If I remove the reference to the real implementation, I just get a good ol' NullPointerException.
I tried playing with the #Tested and #Injectable annotations but to no avail.
Solving my own question.
The problem with the way I wrote the class under test was explicitly invoking the constructor of the real implementation. I'll be surprised if any mocking framework can "bypass" that.
Instead, I should've created a constructor that gets ICouchConnector as one of its arguments, e.g. use dependency injection properly.
public class GraphConnectorImpl implements IGraphConnector {
private static ICouchConnector couch;
public GraphConnectorImpl(ICouchConnector connector) {
couch = connector;
}
// Rest of class...
}
JMockit will then attempt to find a constructor that corresponds to the fields annotated #Tested and #Injectable in the test class.
public class GraphConnectorTest {
#Tested
GraphConnectorImpl graph;
#Injectable
ICouchConnector couch;
// Rest of class...
}
When to create a Stub, start Registry and specify Codebase?
I have created a RMI application. My simple application works. I have the RemoteObjInterface.class's package in my buildpath for the Client and the Server packages. I first start the Server application and then the Client application.
However, I have looked at other examples in the internet and I see them starting the registry, creating a Stub and specifying a codebase.
The following is my program:
The "RemoteObjInterface.class" is my Interface, "RemoteObjImplementation.class" is my Server and "Client.class" is my Client.
public interface RemoteObjInterface extends Remote {
public String someMethod() throws RemoteException;
}
public class RemoteObjImplementation extends UnicastRemoteObject implements
RemoteObjInterface {
private static final long serialVersionUID = 1L;
private static final int PORT = 1099;
private static Registry registry;
public RemoteObjImplementation() throws RemoteException {
super();
}
#Override
public String someMethod() throws RemoteException {
return new String("Hello");
}
public static void main(String args[]) {
Registry registry = LocateRegistry.createRegistry(PORT);
registry.bind(RemoteObjInterface.class.getSimpleName(),
new RemoteObjImplementation());
}
}
public class Client {
private static final String HOST = "localhost";
private static final int PORT = 1099;
private static Registry registry;
public static void main(String[] args) throws Exception {
registry = LocateRegistry.getRegistry(HOST, PORT);
RemoteObjInterface remoteApi = (RemoteObjInterface) registry.lookup(RemoteObjInterface.class.getSimpleName());
System.out.println("Message = " +
remoteApi.someMethod();
}
}
When to create a Stub
Creating a stub is a side-effect of exporting the remote object, which in turn is a side-effect of constructing it if it extends UnicastRemoteObject.
start Registry
When you want to start it. Before you start calling bind() or rebind() for example.
and specify Codebase?
You don't need to use this feature at all, it is optional. If you want your clients to be able to download classes dynamically rather than distributing them to the client ahead of time, specify the java.rmi.server.codebase system property in the server JVM before you export any remote objects (including Registries), and make sure it points to a URL that is accessible by both the Registry and the client.