In JUnit 5, how to run code before all tests

后端 未结 5 2091
悲&欢浪女
悲&欢浪女 2020-12-03 00:34

The @BeforeAll annotation marks a method to run before all tests in a class.

http://junit.org/junit5/docs/current/user-guide/#writing-tests-ann

相关标签:
5条回答
  • 2020-12-03 00:53

    Extending on suggestion from @Philipp, here's a more complete code snippet:

    import static org.junit.jupiter.api.extension.ExtensionContext.Namespace.GLOBAL;    
    import org.junit.jupiter.api.extension.BeforeAllCallback;
    import org.junit.jupiter.api.extension.ExtensionContext;
    
    public abstract class BaseSetupExtension
        implements BeforeAllCallback, ExtensionContext.Store.CloseableResource {
    
      @Override
      public void beforeAll(ExtensionContext context) throws Exception {
        // We need to use a unique key here, across all usages of this particular extension.
        String uniqueKey = this.getClass().getName();
        Object value = context.getRoot().getStore(GLOBAL).get(uniqueKey);
        if (value == null) {
          // First test container invocation.
          context.getRoot().getStore(GLOBAL).put(uniqueKey, this);
          setup();
        }
      }
    
      // Callback that is invoked <em>exactly once</em> 
      // before the start of <em>all</em> test containers.
      abstract void setup();
    
      // Callback that is invoked <em>exactly once</em> 
      // after the end of <em>all</em> test containers.
      // Inherited from {@code CloseableResource}
      public abstract void close() throws Throwable;
    }
    

    How to use:

    public class DemoSetupExtension extends BaseSetupExtension {
      @Override
      void setup() {}
    
      @Override
      public void close() throws Throwable {}
    }  
    
    @ExtendWith(DemoSetupExtension.class)
    public class TestOne {
       @BeforeAll
       public void beforeAllTestOne { ... }
    
       @Test
       public void testOne { ... }
    }
    
    @ExtendWith(DemoSetupExtension.class)
    public class TestTwo {
       @BeforeAll
       public void beforeAllTestTwo { ... }
    
       @Test
       public void testTwo { ... }
    }
    

    Test execution order will be:

      DemoSetupExtension.setup (*)
      TestOne.beforeAllTestOne
      TestOne.testOne
      TestOne.afterAllTestOne
      TestTwo.beforeAllTestTwo
      TestTwo.testTwo
      TestTwo.afterAllTestTwo
      DemoSetupExtension.close (*)
    

    ...this will be true regardless if you choose to run a single @Test (e.g. TestOne.testOne), or an entire test class (TestOne), or multiple / all tests.

    0 讨论(0)
  • 2020-12-03 00:56

    You can mark each of your test classes that uses your database with an interface that defines a static BeforeAll (so that it cannot be overridden). e.g.:

    interface UsesDatabase {
        @BeforeAll
        static void initializeDatabaseConnections() {
            // initialize database connections
        }
    }
    

    This method will be invoked once for each implementing class so you will need to define a way to initialize your connections only once and then do nothing for the other calls.

    0 讨论(0)
  • 2020-12-03 00:57

    Above advises do not work for me, so I solved this problem like this:

    Add to your Base abstract class (I mean abstract class where you initialize your driver in setUpDriver() method) this part of code:

    private static boolean started = false;
    static{
        if (!started) {
            started = true;
            try {
                setUpDriver();  //method where you initialize your driver
            } catch (MalformedURLException e) {
            }
        }
    }
    

    And now, if your test classes will extends from Base abstract class -> setUpDriver() method will be executed before first @Test only ONE time per project run.

    0 讨论(0)
  • 2020-12-03 01:06

    I am not aware of a mean to do that.

    I would simply make sure that all code for @BeforeAll calls a certain singleton to make that init work (probably in a lazy way to avoid repetition).

    Probably not convenient ... the only other option I see: I assume your tests run within a specific JVM job. You could hook an agent into that JVM run, that does that init work for you.

    Beyond that: both suggestions sounds somehow like a hack to me. The real answer in my eyes: step back, and carefully examine your environment on its dependencies. And then find a way to prepare your environment in a way that your tests come up and the "right thing" happens automatically. In other words: consider looking into the architecture that bought you this problem.

    0 讨论(0)
  • 2020-12-03 01:08

    This is now possible in JUnit5 by creating a custom Extension, from which you can register a shutdown hook on the root test-context.

    Your extension would look like this;

    import org.junit.jupiter.api.extension.BeforeAllCallback;
    import org.junit.jupiter.api.extension.ExtensionContext;
    import static org.junit.jupiter.api.extension.ExtensionContext.Namespace.GLOBAL;
    
    public class YourExtension implements BeforeAllCallback, ExtensionContext.Store.CloseableResource {
    
        private static boolean started = false;
    
        @Override
        public void beforeAll(ExtensionContext context) {
            if (!started) {
                started = true;
                // Your "before all tests" startup logic goes here
                // The following line registers a callback hook when the root test context is shut down
                context.getRoot().getStore(GLOBAL).put("any unique name", this);
            }
        }
    
        @Override
        public void close() {
            // Your "after all tests" logic goes here
        }
    }
    

    Then, any tests classes where you need this executed at least once, can be annotated with:

    @ExtendWith({YourExtension.class})
    

    When you use this extension on multiple classes, the startup and shutdown logic will only be invoked once.

    0 讨论(0)
提交回复
热议问题