JBehave
  1. JBehave
  2. JBEHAVE-936

using spring's <jdbc:embedded-database> tag calls the init script twice

    Details

    • Type: Bug Bug
    • Status: Open Open
    • Priority: Major Major
    • Resolution: Unresolved
    • Affects Version/s: 3.8
    • Fix Version/s: None
    • Component/s: Spring Support
    • Labels:
      None
    • Number of attachments :
      0

      Description

      For my tests to run, I need a database. Since I like the speed of H2 and the ability to start with a clean database of in-memory databases, I want to use Spring Frameworks jdbc:embedded-database construct.
      I have the following in my my-steps.xml

      <jdbc:embedded-database id="rxDataSource" type="H2">
       <jdbc:script location="classpath:rx.sql" />
      </jdbc:embedded-database>
      

      The file rx.sql contains SQL like "create table" and "insert into table" to get the database schema and sample data correct.
      When I run mvn integration-test or the MyStory runner, then I get an error that the first table in the list is already defined.
      I've traced it down to somehow the spring container is getting refresh() called and so it goes through and re-processes all the bean definitions, including running the sql script twice.

      I can clearly see (when I turn org.springframework logging to debug) that the script is successfully run once, then later on it's called again.

      Since this same technique is used in more traditional junit/spring based integration tests, I conclude that the issue is with jbehave.

        Activity

        Hide
        Stephen Cooper added a comment -

        It looks like context.refresh() does get called twice - on two different application contexts. But since the H2 database is in memory, it has already been initialized.
        So it looks like the issue is that the context gets initialized twice with the jbehave-spring archetype.
        Here's the first initialization:

        main@1, prio=5, in group 'main', status: 'RUNNING'
        	  at org.jbehave.core.steps.spring.SpringApplicationContextFactory.createApplicationContext(SpringApplicationContextFactory.java:72)
        	  at com.ften.rx.jbehave.MyStories.stepsFactory(MyStories.java:77)
        	  at org.jbehave.core.ConfigurableEmbedder.configuredEmbedder(ConfigurableEmbedder.java:88)
        	  at com.ften.rx.jbehave.MyStories.<init>(MyStories.java:50)
        	  at sun.reflect.NativeConstructorAccessorImpl.newInstance0(NativeConstructorAccessorImpl.java:-1)
        	  at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
        	  at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
        	  at java.lang.reflect.Constructor.newInstance(Constructor.java:525)
        	  at org.junit.runners.BlockJUnit4ClassRunner.createTest(BlockJUnit4ClassRunner.java:209)
        	  at org.junit.runners.BlockJUnit4ClassRunner$1.runReflectiveCall(BlockJUnit4ClassRunner.java:258)
        

        here's the second call to refresh()

        main@1, prio=5, in group 'main', status: 'RUNNING'
        	  at org.jbehave.core.steps.spring.SpringApplicationContextFactory.createApplicationContext(SpringApplicationContextFactory.java:72)
        	  at com.ften.rx.jbehave.MyStories.stepsFactory(MyStories.java:77)
        	  at org.jbehave.core.ConfigurableEmbedder.configuredEmbedder(ConfigurableEmbedder.java:88)
        	  at org.jbehave.core.junit.JUnitStories.run(JUnitStories.java:18)
        	  at sun.reflect.NativeMethodAccessorImpl.invoke0(NativeMethodAccessorImpl.java:-1)
        	  at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
        	  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        	  at java.lang.reflect.Method.invoke(Method.java:601)
        	  at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
        	  at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
        	  at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
        	  at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
        

        Perhaps the MyStories<init> shouldn't be calling configuredEmbedder, but it would appear that JUnitStories.run calls configuredEmbedder for each story, so this will probably be an issue once I get past the first story.
        The Spring test framework has solutions around this, including using the same ApplicationContext across tests unless you call @DirtiesContext. And when the context is shut down, the in-memory database is disposed.

        Show
        Stephen Cooper added a comment - It looks like context.refresh() does get called twice - on two different application contexts. But since the H2 database is in memory, it has already been initialized. So it looks like the issue is that the context gets initialized twice with the jbehave-spring archetype. Here's the first initialization: main@1, prio=5, in group 'main', status: 'RUNNING' at org.jbehave.core.steps.spring.SpringApplicationContextFactory.createApplicationContext(SpringApplicationContextFactory.java:72) at com.ften.rx.jbehave.MyStories.stepsFactory(MyStories.java:77) at org.jbehave.core.ConfigurableEmbedder.configuredEmbedder(ConfigurableEmbedder.java:88) at com.ften.rx.jbehave.MyStories.<init>(MyStories.java:50) at sun.reflect.NativeConstructorAccessorImpl.newInstance0(NativeConstructorAccessorImpl.java:-1) at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57) at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) at java.lang.reflect.Constructor.newInstance(Constructor.java:525) at org.junit.runners.BlockJUnit4ClassRunner.createTest(BlockJUnit4ClassRunner.java:209) at org.junit.runners.BlockJUnit4ClassRunner$1.runReflectiveCall(BlockJUnit4ClassRunner.java:258) here's the second call to refresh() main@1, prio=5, in group 'main', status: 'RUNNING' at org.jbehave.core.steps.spring.SpringApplicationContextFactory.createApplicationContext(SpringApplicationContextFactory.java:72) at com.ften.rx.jbehave.MyStories.stepsFactory(MyStories.java:77) at org.jbehave.core.ConfigurableEmbedder.configuredEmbedder(ConfigurableEmbedder.java:88) at org.jbehave.core.junit.JUnitStories.run(JUnitStories.java:18) at sun.reflect.NativeMethodAccessorImpl.invoke0(NativeMethodAccessorImpl.java:-1) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:601) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20) Perhaps the MyStories<init> shouldn't be calling configuredEmbedder, but it would appear that JUnitStories.run calls configuredEmbedder for each story, so this will probably be an issue once I get past the first story. The Spring test framework has solutions around this, including using the same ApplicationContext across tests unless you call @DirtiesContext. And when the context is shut down, the in-memory database is disposed.

          People

          • Assignee:
            Unassigned
            Reporter:
            Stephen Cooper
          • Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

            Dates

            • Created:
              Updated: