'Address already in use' when running tests using Spring LDAP embedded server
Asked Answered
S

7

9

I am trying to use Spring LDAP in one of my Spring Boot projects but I am getting an 'Address already in use' error when running multiple tests.

I have cloned locally the sample project here: https://spring.io/guides/gs/authenticating-ldap/

...and just added the boilerplate test normally created by Spring Boot to verify that the Application Context loads correctly:

@RunWith(SpringRunner.class)
@SpringBootTest
public class MyApplicationTests {
    @Test
    public void contextLoads() {
    }
}

If run alone, this test passes. As soon as LdapAuthenticationTests and MyApplicationTests are run together, I get the error above for the latter.

After debugging a bit, I've found out that this happens because the system tries to spawn a second instance of the embedded server.

I am sure I am missing something very stupid in the configuration. How can I fix this problem?

Scrummage answered 14/11, 2017 at 17:28 Comment(0)
A
9

I had a similar problem, and it looks like you had a static port configured (as was in my case).

According to this article:

Spring Boot starts an embedded LDAP server for each application context. Logically, that means, it starts an embedded LDAP server for each test class. Practically, this is not always true since Spring Boot caches and reuses application contexts. However, you should always expect that there is more than one LDAP server running while executing your tests. For this reason, you may not declare a port for your LDAP server. In this way, it will automatically uses a free port. Otherwise, your tests will fail with “Address already in use”

Thus it might be a better idea not to define spring.ldap.embedded.port at all.

Alduino answered 10/1, 2019 at 16:33 Comment(3)
Can you please show your ldap configuration methods? How do you set the port in non-testing environment. There is just testing part covered in the article.Perineuritis
@JanCizmar The prod port is set in application.properties: ldap.url=ldap://{domain}:{port}/DC={domaincomponent1},DC={domaincomponent2} Security configuration is like (sorry for broken formatting): auth.ldapAuthentication().userDetailsContextMapper(InetOrgPersonContextMapper()).userSearchBase({userSearchBaseConfig}).userSearchFilter({userSearchFilterConfig}).groupSearchBase({groupSearchBaseConfig}).contextSource().url({urlConfig}).managerDn({managerDnConfig}).managerPassword({managerPasswordConfig}) Alduino
Perfect!!! I just removed spring.ldap.embedded.port and it start working.Swizzle
F
7

I addressed the same issue. I solved it with an additional TestExecutionListener since you can get the InMemoryDirectoryServer bean.

/**
 * @author slemoine
 */
public class LdapExecutionListener implements TestExecutionListener {

    @Override
    public void afterTestClass(TestContext testContext) {
        InMemoryDirectoryServer ldapServer = testContext.getApplicationContext().getBean(InMemoryDirectoryServer.class);
        ldapServer.shutDown(true);
    }
}

And on each SpringBootTest (or only once in an abstract super class)

@RunWith(SpringRunner.class)
@SpringBootTest
@TestExecutionListeners(listeners = LdapExecutionListener.class,
        mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS)
public class MyTestClass {

...

}

also do not forget

mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS

to avoid disabling the whole @SpringBootTest auto configuration.

Fikes answered 6/3, 2019 at 14:52 Comment(1)
This is should be the correct answer. I tried the above solutions and non of them worked for my case. Thank you for sharing this great solution.Chairmanship
S
6

Okay, I think I found a solution by adding a @DirtiesContext annotation to my test classes:

@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)

Scrummage answered 15/11, 2017 at 13:47 Comment(2)
Good idea. Unfortunately the error still happens when trying to launch the tests while application is running.Alduino
That could be solved by overriding application properties with specific port for tests.Perineuritis
M
1

If you are using spring embedded ldap, try to comment or remove port value from config file as below :

spring :
  ldap:
    embedded:
      base-dn: dc=example,dc=org
      credential:
        username: cn=admin,dc=example,dc=org
        password: admin
      ldif: classpath:test-schema.ldif
      # port: 12345
      validation:
        enabled: false
Michaeu answered 9/10, 2020 at 11:47 Comment(0)
F
0

Try specifying the web environment type and the base configuration class (the one with !SpringBootApplication on it).

@RunWith(SpringRunner.class)
@SpringBootTest(
    classes = MyApplication.class,
    webEnvironment = RANDOM_PORT
)
public class MyApplicationTests {
    @Test
    public void contextLoads() {
    }
}

Do this for all your test classes.

Freewill answered 14/11, 2017 at 17:34 Comment(3)
That doesn't fix the problem. I think it's because the ldap server is started every time an application context is loaded, which I guess is the case with SpringRunner? Not sure if manually tearing down on an @AfterClass-annotated method would fix the problem - even if it worked it would be annoying as every test class should have that... :-(...Scrummage
Ah yes, I missed that you were using embedded ldap (sorry). If you are using the default embedded ldap configuration and since the ldap port is not random, you would end up with problems if the embedded ldap is not cleaned before starting the next set of tests, but also if you run your tests in parallel. I would create a test configuration that sets a random port for ldap as well (like maybe the random web port+1).Freewill
I tried that as well. Although it fixes the socket binding problem by getting a new port each time, I cannot find a valid configuration without using .url() - and if I need to pass a url, I need to pass a port as well (which I don't get to know as it's generated randomly). Although the docs say that the url should be passed only when not using the embedded server, it is used in the example they provide and I couldn't manage to make the app work without it. Also, by assigning a random port, does that mean a new embedded server is launched per test class? (which would be scary)Scrummage
L
0

I solved this problem by adding @DirtiesContext over each test class that requires embedded ldap server. In my case (and as I feel in many others), embedded ldap server was starting up at every @SpringBootTest, since I added all spring.ldap.embedded.* properties to general application-test.properties. Therefore, when I run a bunch of tests, the problem of 'Address already in use' broke all test passing.

Steps I followed:

  • create an additional test profile (with corresponding named application properties file, e.g. 'application-ldaptest.properties')

  • move to that file all spring.ldap.embedded.* properties (with fixed port value)

  • over all @SpringBootTest-s that do require embedded server running up, add @ActiveProfiles("testladp") and @DirtiesContext annotations.

Hope, that helps.

Lovettalovich answered 11/11, 2022 at 10:21 Comment(0)
K
0

There are other ways you can get OP's error, such as when you need to support multiple ldap configurations. There is another trick you can apply to work around the problem.

1․ Keep the spring.ldap.embedded.port for `standard' ldap tests.

2․ Use the @TestPropertySource annotation for `alternative' ldap tests:

@SpringBootTest
@TestPropertySource(properties = {"spring.ldap.embedded.port=11111"})
public class SomeTest {
  // your tests
}
Kyongkyoto answered 2/5 at 11:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.