spring-projects / spring-shell

Spring based shell
http://projects.spring.io/spring-shell/
Apache License 2.0
728 stars 394 forks source link

Trouble Testing in Spring Shell 2 #171

Closed jgreub closed 6 years ago

jgreub commented 6 years ago

Hey, I just stumbled upon Spring Shell recently and I am unable to get a @SpringBootTest test to execute. The test attempts to begin, but hangs in the IntelliJ console as if a shell has been created and waiting for input.

Test:

@RunWith(SpringRunner.class)
@SpringBootTest
public class RecordScoreTest {

    @Test
    public void simpleTest() {
        Assertions.assertThat(true).isTrue();
    }
}

Console:

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.5.8.RELEASE)

2017-10-22 07:56:01.498  INFO 11233 --- [           main] c.j.bowlinggamekata.RecordScoreTest      : Starting RecordScoreTest on JoeMacPro.local with PID 11233 (started by joe in /Users/joe/workspace/bowling-game-kata)
2017-10-22 07:56:01.500  INFO 11233 --- [           main] c.j.bowlinggamekata.RecordScoreTest      : No active profile set, falling back to default profiles: default
2017-10-22 07:56:01.534  INFO 11233 --- [           main] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@5fbe4146: startup date [Sun Oct 22 07:56:01 PDT 2017]; root of context hierarchy
2017-10-22 07:56:02.043  INFO 11233 --- [           main] trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.shell.SpringShellAutoConfiguration' of type [org.springframework.shell.SpringShellAutoConfiguration$$EnhancerBySpringCGLIB$$f25f3e65] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2017-10-22 07:56:02.062  INFO 11233 --- [           main] trationDelegate$BeanPostProcessorChecker : Bean 'conversionService' of type [org.springframework.core.convert.support.DefaultConversionService] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2017-10-22 07:56:02.437  WARN 11233 --- [           main] org.jline                                : Unable to create a system terminal, creating a dumb terminal (enable debug logging for more information)

Here's a link to my tiny repo that acts as the simplest way to reproduce this issue: https://github.com/jgreub/bowling-game-kata I'm trying to use Spring Shell 2.0.0.M2 with Spring Boot 1.5.8.RELEASE


As a follow up

When it comes to actually testing a @ShellMethod, this stackoverflow post claims you use the Shell#evaluate() method. How do you obtain an instance of Shell in a test?

girishla commented 6 years ago

I'm the OP of that stackoverflow thread. I got this working but forgot to update the thread.

There may be better ways but this is how I got it working.

You first need to override the default shell application runner to avoid getting stuck in the jline loop. You can do this by defining your own such as:

@Component
public class CliAppRunner implements ApplicationRunner {
    public CliAppRunner() {

    }

    @Override
    public void run(ApplicationArguments args) throws Exception {
        //do nothing
    }

}

Note that you will have to associate this custom Application runner against a "Test" profile so it overrides only during integration testing.

You then can write a test like this:


@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes =CliConfig.class)
public class ShellCommandIntegrationTest {

    @Autowired
    private Shell shell;

    @Test
    public void runTest(){

        Object result=shell.evaluate(new Input(){
            @Override
            public String rawText() {
                return "add 1 3";
            }

        });

        DefaultResultHandler  resulthandler=new DefaultResultHandler();
        resulthandler.handleResult(result);

    }

}

Note that the above test does not Assert anything. You will probably have to write your own little implementation of the ResultHandler interface that deals with parsing/formatting of the result so that it can be asserted.

Hope it helps.

jgreub commented 6 years ago

Hey @girishla thanks for the tips, I managed to get it working with your help. Here's what I did:

@TestConfiguration
public class TestApplicationRunner implements ApplicationRunner {

    private static Logger log = LoggerFactory.getLogger(TestApplicationRunner.class);

    public TestApplicationRunner() {
        log.info("Test Application Runner started!");
    }

    @Override
    public void run(ApplicationArguments args) throws Exception {
        log.info("About to do nothing!");
        // Do nothing...
    }

}
@RunWith(SpringRunner.class)
@SpringBootTest
@Import(TestApplicationRunner.class)
public class RecordScoreTest {

    @Autowired
    private Shell shell;

    @Test
    public void playerCanRecordEntireScoreOfGame() {
        assertThat(shell.evaluate(() -> "record 7")).isEqualTo("Your bowling game score is 7! Well done!");
    }
}

Notice the @Import(TestApplicationRunner.class) annotation on the test class. By adding this configuration, the output in the console now looks like this:

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.5.8.RELEASE)

2017-10-29 14:51:53.787  INFO 10232 --- [           main] c.j.bowlinggamekata.RecordScoreTest      : Starting RecordScoreTest on JoeMacPro.local with PID 10232 (started by joe in /Users/joe/workspace/bowling-game-kata)
2017-10-29 14:51:53.788  INFO 10232 --- [           main] c.j.bowlinggamekata.RecordScoreTest      : No active profile set, falling back to default profiles: default
2017-10-29 14:51:53.811  INFO 10232 --- [           main] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@2d2ffcb7: startup date [Sun Oct 29 14:51:53 PDT 2017]; root of context hierarchy
2017-10-29 14:51:54.253  INFO 10232 --- [           main] trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.shell.SpringShellAutoConfiguration' of type [org.springframework.shell.SpringShellAutoConfiguration$$EnhancerBySpringCGLIB$$a942d27c] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2017-10-29 14:51:54.269  INFO 10232 --- [           main] trationDelegate$BeanPostProcessorChecker : Bean 'conversionService' of type [org.springframework.core.convert.support.DefaultConversionService] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2017-10-29 14:51:54.533  INFO 10232 --- [           main] c.j.b.helpers.TestApplicationRunner      : Test Application Runner started!
2017-10-29 14:51:54.631  WARN 10232 --- [           main] org.jline                                : Unable to create a system terminal, creating a dumb terminal (enable debug logging for more information)
2017-10-29 14:51:54.806  INFO 10232 --- [           main] c.j.b.helpers.TestApplicationRunner      : About to do nothing!
2017-10-29 14:51:54.807  INFO 10232 --- [           main] c.j.bowlinggamekata.RecordScoreTest      : Started RecordScoreTest in 1.507 seconds (JVM running for 2.132)
2017-10-29 14:51:54.941  INFO 10232 --- [       Thread-2] s.c.a.AnnotationConfigApplicationContext : Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@2d2ffcb7: startup date [Sun Oct 29 14:51:53 PDT 2017]; root of context hierarchy

Process finished with exit code 0
brenthaertlein commented 6 years ago

@girishla Your comment (https://github.com/spring-projects/spring-shell/issues/171#issuecomment-340267898) was incredibly helpful. Thanks!

tschuege commented 6 years ago

@girishla In your solution you mentioned "Note that you will have to associate this custom Application runner against a "Test" profile so it overrides only during integration testing." How would you do that?

girishla commented 6 years ago

@tschuege you can use @ActiveProfiles annotation on your AppRunner implementation class or another easier way would be to use @TestConfiguration on it

zygimantus commented 4 years ago

Using this approach for me test was started, but not finished:

019-12-14 12:44:41.798 INFO 18872 --- [ main] c.z.a.TestApplicationRunner : Test Application Runner started! 2019-12-14 12:44:43.137 WARN 18872 --- [ main] org.jline : Unable to create a system terminal, creating a dumb terminal (enable debug logging for more information) 2019-12-14 12:44:43.387 INFO 18872 --- [ main] c.demo.CommandTest : Started CommandTest in 2.548 seconds (JVM running for 3.448) shell:>

It actually seems that shell is opened during test execution and waiting for user input.

jhalehol commented 1 year ago

@zygimantus you should disable interactive into the SpringBootTest properties

@SpringBootTest(properties = "spring.shell.interactive.enabled=false")