spring-cloud/spring-cloud-core-tests

ribbon-eureka: start Eureka and SimpleApplication programmatically

nyg opened this issue · 1 comments

nyg commented

concerns ribbon-eureka

Hello,

To run the HelloClientApplicationTest test successfully I need to manually start EurekaServerApplication and SimpleApplication. HelloClientApplication will be started before the test by the @SpringApplicationConfiguration annotation.

My question is, how can I avoid manually starting both EurekaServerApplication and SimpleApplication? I've tried two things:

// HelloClientApplicationTests.java
public void clientConnects() throws Exception {

    new Thread(
        () -> new SpringApplicationBuilder(EurekaServerApplication.class)
            .properties("spring.config.name:eureka", "logging.level.com.netflix.discovery:OFF")
            .run()).start();

    new Thread(
        () -> new SpringApplicationBuilder(SimpleApplication.class)
            .properties("spring.config.name:simple")
            .run()).start();

    ResponseEntity<String> response = new TestRestTemplate().getForEntity("http://localhost:" + port, String.class);
    assertNotNull("response was null", response);
    assertEquals("Wrong status code", HttpStatus.OK, response.getStatusCode());
    assertEquals("Hello", response.getBody());
}

and

public void clientConnects() throws Exception {

    exec(EurekaServerApplication.class);
    Thread.sleep(20 * 1000);

    exec(SimpleApplication.class);
    Thread.sleep(20 * 1000);

    ResponseEntity<String> response = new TestRestTemplate().getForEntity("http://localhost:" + port, String.class);
    assertNotNull("response was null", response);
    assertEquals("Wrong status code", HttpStatus.OK, response.getStatusCode());
    assertEquals("Hello", response.getBody());
}

// http://stackoverflow.com/questions/636367/executing-a-java-application-in-a-separate-process
public static void exec(Class klass) throws IOException, InterruptedException {
    String javaHome = System.getProperty("java.home");
    String javaBin = javaHome + File.separator + "bin" + File.separator + "java";
    String classpath = System.getProperty("java.class.path");
    String className = klass.getCanonicalName();
    new ProcessBuilder(javaBin, "-cp", classpath, className).start();
}

What's interesting with the last piece of code (not sure how portable it is) is that after the test has failed, the two created process are still running and can be reached. For some reason I can't reach them while the test is running (even with a breakpoint). And neither can HelloClientApplication...

The reason I want to do this is for the tests to be run by some continuous integration software (we use Bamboo).

Thanks a lot,
nyg

nyg commented

Ok, I manage to get it working, the trick was to use redirectError() and redirectOutput() on the process builder... However, I'd still like your input on how good (or not) my solution is :).

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = HelloClientApplication.class)
@WebIntegrationTest(randomPort = true)
public class HelloClientApplicationTests {

    @Autowired
    EurekaDiscoveryClient discoveryClient;

    @Value("${local.server.port}")
    int port;

    @Test
    public void clientConnects() throws Exception {

        Process eureka = exec(EurekaServerApplication.class, "eureka_logs.txt");
        Process simple = exec(SimpleApplication.class, "simple_logs.txt");

        while (!discoveryClient.getServices().containsAll(Arrays.asList("helloclient", "simple"))) {
            Thread.sleep(500);
        }

        ResponseEntity<String> response = new TestRestTemplate().getForEntity("http://localhost:" + port, String.class);
        assertNotNull("Response was null!", response);
        assertEquals("Wrong status code!", HttpStatus.OK, response.getStatusCode());
        assertEquals("Hello", response.getBody());

        eureka.destroyForcibly();
        simple.destroyForcibly();
    }

    // http://stackoverflow.com/questions/636367/executing-a-java-application-in-a-separate-process
    private Process exec(Class<?> klass, String fileName) throws IOException {
        String javaHome = System.getProperty("java.home");
        String javaBin = javaHome + File.separator + "bin" + File.separator + "java";
        String classPath = System.getProperty("java.class.path");
        String className = klass.getCanonicalName();
        File processOutput = new File(fileName);
        return new ProcessBuilder(javaBin, "-cp", classPath, className).redirectOutput(processOutput).redirectError(processOutput).start();
    }
}