Container not starting with GenericContainer - testcontainers

I have just started using testcontainers and I am getting into issues. Below is my code:
#ClassRule
public static GenericContainer redisContainer =
new GenericContainer("corporateurl/redis:latest")
.withExposedPorts(6379);
When I run this without the exposedPorts it works fine but with exposedPorts it gives an error:
Could not start container
java.lang.IllegalStateException: Container did not start correctly.
Any idea how to prevent that?

It's really hard to say since the exception doesn't provide any clues, I would suggest you try accessing logs:
https://www.testcontainers.org/features/container_logs/
Is 6379 available on localhost at the moment you start your test, any chance there is some other container?

Related

Start a Testcontainers container during each fresh application context (#DirtiesContext), with injection of properties (#DynamicPropertySource)

I am using Testcontainers to execute integration tests in a Spring project. I am using JUnit 4.x and Cucumber 7.4.1.
I want to start a docker-compose.yml-based set of containers before each test, as that makes it easy to start from scratch. Hence, I am using #DirtiesContext.
The challenge here is that certain application properties, such as spring.rabbitmq.host, are needed before the actual application context can start. So I need to inject them beforehand. There is #DynamicPropertySource for that. But then I also need to get access to my context-scoped docker containers. The best I came up with so far is the following:
#CucumberContextConfiguration
#SpringBootTest(classes = TestConfig.class)
#DirtiesContext
public class CucumberITConfig {
#DynamicPropertySource
private static void properties(DynamicPropertyRegistry registry) {
DockerComposeContainer container = new DockerComposeContainer(new File("docker-compose.yml"))
.withExposedService("rabbitmq", 5672);
container.start();
registry.add("spring.rabbitmq.host", () -> container.getServiceHost("rabbitmq", 5672));
}
}
This constructs new docker containers locally and waits for the host to be passed to the registry. While this seem to work, this looks more like a hackish approach to me. Also, a problem here is that the containers stack up after each test. That is, in the 7th test, for example, the containers from all previous 6 cycles are still running.
Are there better approaches to start Testcontainers-based docker containers before each application context, while also being able to destruct them afterwards?
If you are using #DirtiesContext after all, you can set these values as System properties and omit #DynamicPropertySource altogether. However, as others have pointed out, solving test pollution by re-creating the Spring context and all dependent services for every test class will be very slow and is generally considered an anti-pattern.

CI/CD Implementation in WebSphere Liberty 19.0.0.8 server with Maven

Am developing application using Maven it has EJB layer. I configured datasource in WebSphere Liberty server. All transaction can be handle by the server. Am using Jenkins to build a application. I would like to create CI/CD implementation. For that I tried add Junit test in application. but am unable to connect database while doing Jenkins build. Because there is no communication b/w server and Jenkins while doing build. How can I create Junit that handle database connection and EJB without Mock.?
One possible solution you could use here is an integration testing library called MicroShed Testing. It is created for integration tests with JavaEE/MicroProfile app servers such as Liberty, and can be used to test your application with external resources running such as DBs.
MicroShed Testing is ideal if you are running your Liberty application inside of a Docker container. If you are running in Docker, you can easily write an integration test that looks something like this:
#MicroShedTest
#SharedContainerConfig(AppContainerConfig.class)
public class DatabaseTest {
#Inject
public static MyJAXRSEndpoint personSvc;
#Test
public void testGetPerson() {
Long bobId = personSvc.createPerson("Bob", 24);
Person bob = personSvc.getPerson(bobId);
assertEquals("Bob", bob.name);
assertEquals(24, bob.age);
}
}
To get this working, you can define your application topology in the class referenced by the #SharedContainerConfig annotation like this:
public class AppContainerConfig implements SharedContainerConfiguration {
#Container
public static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>()
.withNetworkAliases("testpostgres")
.withDatabaseName("testdb");
#Container
public static MicroProfileApplication app = new MicroProfileApplication()
.withEnv("POSTGRES_HOSTNAME", "testpostgres")
.withEnv("POSTGRES_PORT", "5432")
.withAppContextRoot("/myservice");
#Override
public void startContainers() {
postgres.start();
app.start();
}
}
The above code will do the following steps:
1) Build your application into a Docker container, using the Dockerfile in your repo
2) Start up the docker container for your app AND the postgresql database (or any other DB container you may need)
3) Wait for containers to be ready, then run tests that invoke HTTP requests on the running containers
I find this approach nice because it works the same way anywhere Docker is installed -- either locally on your machine or in your CI/CD pipeline.
For more info on MicroShed Testing, I recommend checking out the website here:
https://microshed.org/microshed-testing/
and especially the examples, which include a Liberty+Database example:
https://microshed.org/microshed-testing/features/99_Examples.html
Disclaimer: I work on Liberty and MicroShed Testing

Access to h2 web console while running junit test in a Spring application

I'm building a Spring application and I need to inspect my H2 in-memory database while I'm running my JUnit tests from a web browser.
In my Spring configuration I have a bean which is responsible of creating my database schema and populating it with some data which will be used within my JUnit tests. I've also added a bean in my test context which creates a web server where I eventually will look for my data.
<bean id="org.h2.tools.Server-WebServer" class="org.h2.tools.Server"
factory-method="createWebServer" init-method="start" lazy-init="false">
<constructor-arg value="-web,-webAllowOthers,-webPort,11111" />
</bean>
Everything seems ok because the database is populated properly since I can access to its data from my JUnit tests and H2 Server only runs while I'm in my test-phase (I can know that, because if I try to access to my_ip:111111 before debugging my tests I cannot connnect but I can connect afterwards once I've started my tests).
Anyway If I open my H2 console from a web browser no schema is shown in it. Any ideas??
Many thanks!!
As this is probably going to be a test-debugging feature, you can add it at runtime with your #Before:
import org.h2.tools.Server;
/* Initialization logic here */
#BeforeAll
public void initTest() throws SQLException {
Server.createWebServer("-web", "-webAllowOthers", "-webPort", "8082")
.start();
}
And then connect to http://localhost:8082/
Note: unless you need this to run as part of your CI build, you'll need to remove this code when you're finished debugging
For future reference here's another way to do it:
Start database and web servers (version can differ):
$ cd .../maven_repository/com/h2database/h2/1.4.194
$ java -cp h2-1.4.194.jar org.h2.tools.Server -tcp -web -browser
TCP server running at tcp://169.254.104.55:9092 (only local connections)
Web Console server running at http://169.254.104.55:8082 (only local connections)
Set database url for tests in code to jdbc:h2:tcp://localhost:9092/mem:mytest.
Run or debug tests.
Click Connect in browser window which opened in step 1.
Jar file for H2 can be downloaded at https://mvnrepository.com/artifact/com.h2database/h2.
Server can be started via #Before in test file like in snovelli's answer, but only in case connection to database in established afterwards, which might be a problem.
I guess the problem is that you are connecting to h2db directly from your application. Not through the server you are launching with bean. Because of this your app and h2db-web-interface can't share one in-memory database.
You should change jdbcUrl in tests to something like jdbc:h2:tcp://localhost/mem:my_DB;DB_CLOSE_DELAY=-1;MODE=Oracle and in browser you should connect to the same url.
With jdbc urls like jdbc:h2:tcp://localhost/... all connections will go through the h2db-server and you can view database state in browser.
If you have defined the jdbc url to something like jdbc:h2:mem:db in your properties, when the database is created it actually gets a bit longer name.
Add a #Autowired DataSource dataSource to your test class, set a debug point somewhere, and inspect that datasource with dataSource.getConnection() and look at the url property. In the case I'm running right this moment, it is
jdbc:h2:mem:43ed83d6-97a1-4515-a925-a8ba53cd322c
Plugging that into the web cosole shows everything I'm expecting.
It isn't the most straightforward way, but it does work.
#snovelli answer above is good.
To debug a particular test case in your IDE, add a infinite loop at the end of the test case and go to browser and launch the console and you can query the data.
Something like below
import org.h2.tools.Server;
/* Initialization logic here */
#BeforeAll
public void initTest() throws SQLException {
Server.createWebServer("-web", "-webAllowOthers", "-webPort", "8082")
.start();
}
#Test
void testMyDBOperation() {
//some db operations like save and get
while(true) {
}
}
now you can go to browser and launch the console at http://localhost:8082/
Of course delete above two changes after debugging
It is not the answer, but a debugging tip.
When you finally access h2-conole http://127.0.0.1:8082/ you may notice that database changes are not shown.
This is because the test cases are not transactional and the data is not committed. Although this behavior is good, as each test case, must run in predefined environment. It is not good if you want to debug and see database changes.
To achieve this, add #Commit annotation above test case and put a dummy line in a #AfterAll annotated method, to stop test and let you see the h2 console ( The h2 server will stop as the test finish).
#AfterAll
public static void finalizeTest() throws Exception {
System.out.print("Just put a break point here");
}
#Test
#Commit
void should_store_an_article() {
// Your test here
}

Problems with #Transactional and JerseyTest on Linux or Mac but not on Windows

We use jersey test with grizzly2 to run acceptence tests against mocked REST resources. On my windows machine everything's fine. But another developer with his Mac is getting the same error as our Jenkins (on Linux):
INFO: Creating Grizzly2 Web Container configured at the base URI http://localhost:9998/
02.08.2012 09:46:36 org.glassfish.grizzly.http.server.HttpServer start
SEVERE: Failed to start listener [NetworkListener{name='grizzly', host='localhost', port=9998, secure=false}] : java.net.BindException: Address already in use
Obviously we checked that this is not the case: No other process is using 9998..
I've been tracking the problem down to a single test , that is using #Transactional in combination with extending JerseyTest:
#ContextConfiguration({ "classpath:context-test.xml" })
#RunWith(SpringJUnit4ClassRunner.class)
#Transactional
#TransactionConfiguration(defaultRollback = true)
public class TestPage extends JerseyTest {
public TestPage() throws Exception {
super("", "", "path.to.package");
}
// init database to be rolled back after test
// ...
// test that calls a controller requiring database access and then sends a request to a mock REST resource.
// ...
}
So, the questions here are: Why not on windows? And what's wrong with using #Transactional.
edit2
It seems #Transactional is creating a proxy, this might be a problem..?! See here
I ran into a similar issue, make sure that you are not overRiding the tearDown() method in your TestPage test. If you have something to tear down, do it using a another method customTearDown() and annotate it with #After.
JerseyTest also uses the same method name tearDown() and the container never gets shut down if we override it.

Acceptance testing preloading of data into GAE dev server datastore

In my application I have a set of of DAOs which I inject into my application layer. For an acceptance test I'm writing, I want to preload the dev_server datastore with data, so I use the same Spring config in my JUnit test (using the #ContextConfiguration annotation) to inject an instance of the relevant DAO into my test. When I actually go to store some data eg:
dao.add(entity)
I get the dreaded "No API environment is registered for this thread."
Caused by: java.lang.NullPointerException: No API environment is registered for this thread.
at com.google.appengine.api.datastore.DatastoreApiHelper.getCurrentAppId(DatastoreApiHelper.java:108)
at com.google.appengine.api.datastore.DatastoreApiHelper.getCurrentAppIdNamespace(DatastoreApiHelper.java:118)
....
This is probably because my test case hasn't read in the GAE application-web.xml with the app details (although I'm guessing here I could really be wrong); so it doesn't know to write to the same datastore that the app running on the dev_server is reading/writing to.
How can I get my test to "point" to the same datastore as the app? Is there some "datasource" mechanism that I can inject both into the app and the test? Is there a way to get my test to force the datastore api to read the needed config?
Here is a page that talks about how to do unit tests that connect to a dev datastore. Is this the kind of thing you're looking for? Basically it talks about two classes, LocalServiceTestHelper and LocalDatastoreServiceTestConfig that you can use to set up an environment for testing. While the example given is for unit tests, I believe it will also work for your situation.
You can then configure things like whether the dev datastore is written to disk or just kept in memory (for faster tests). If you want this data to go to the same place as your dev server, you will probably want to adjust this, as I think the default is the "in memory" option. If you look at the javadoc there is a "setBackingStoreLocation" method where you can point to whatever file you want.
I've found the solution!!!!
For some reason the Namespace, AppID and the AuthDomain fields of the test datastore have to match that of the dev_server, then the dev_server can see the entities inserted by the test.
You can see the values for the environment (dev_server or test code) with the following statements
System.out.println(NamespaceManager.get());
System.out.println(ApiProxy.getCurrentEnvironment().getAppId());
System.out.println(ApiProxy.getCurrentEnvironment().getAuthDomain());
In your instance of LocalServiceTestHelper (eg: gaeHelper), you can set the values for the test environment
// the NamespaceManager is thread local.
NamespaceManager.set(NamespaceManager.getGoogleAppsNamespace());
gaeHelper.setEnvAppId(<the name of your app in appengine-web.xml>);
gaeHelper.setEnvAuthDomain("gmail.com");
Then the dev_server will see your entities. However because of synchronisation issues, if the test writes to the datastore after the dev_server has been started the dev_server wont see it unless it can be forced to reread the file (which I haven't figured out yet). Else the server has to be restarted.
I've found a workaround, although it's not very nice because each test method doesn't clean up the Datastore, as explained in the article Local Unit Testing for Java, however, the Datastore starts clean each time the Test class is run, so it's not so bad, provided that you're careful about that.
The problem is, that when using SpringJUnit4ClassRunner, the spring environment is created before the #Before annotation can be run, the solution is use #BeforeClass and use a static variable for LocalServiceTestHelper, to have them created before the Spring Environment is set up.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration("classpath:META-INF/spring/context-test.xml")
#Transactional
public class MyTest {
#Inject
private MyService myService;
private static final LocalServiceTestHelper helper =
new LocalServiceTestHelper(new LocalDatastoreServiceTestConfig());
#BeforeClass
public static void beforeClass() {
helper.setUp();
}
#AfterClass
public static void afterClass() {
helper.tearDown();
}
If anyone has a better solution, I'll be glad to hear!

Resources