From 3b2126f99f53b12fea9ba889d3c53975fdaa3d07 Mon Sep 17 00:00:00 2001 From: Christoph Deppisch Date: Wed, 31 Jan 2024 16:56:19 +0100 Subject: [PATCH] fix(#1115): Create new test context for each Cucumber scenario - Citrus Cucumber Spring object factory must create new test context instance for each scenario - Using a singleton test context instance for all scenarios leads to unexpected behavior (e.g. when exceptions are added to the test context) --- .../spring/CitrusSpringObjectFactory.java | 42 ++++++++++++------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/runtime/citrus-cucumber/src/main/java/org/citrusframework/cucumber/backend/spring/CitrusSpringObjectFactory.java b/runtime/citrus-cucumber/src/main/java/org/citrusframework/cucumber/backend/spring/CitrusSpringObjectFactory.java index bc4093ce21..19f302ccc1 100644 --- a/runtime/citrus-cucumber/src/main/java/org/citrusframework/cucumber/backend/spring/CitrusSpringObjectFactory.java +++ b/runtime/citrus-cucumber/src/main/java/org/citrusframework/cucumber/backend/spring/CitrusSpringObjectFactory.java @@ -48,6 +48,9 @@ public class CitrusSpringObjectFactory implements ObjectFactory { /** Test context */ private TestContext context; + /** Test context factory */ + private TestContextFactoryBean testContextFactory; + /** Static self reference */ private static CitrusSpringObjectFactory selfReference; @@ -64,7 +67,7 @@ public CitrusSpringObjectFactory() { @Override public void start() { delegate.start(); - context = getInstance(TestContext.class); + context = createTestContext(); runner = TestCaseRunnerFactory.createRunner(context); } @@ -75,17 +78,6 @@ public void stop() { @Override public T getInstance(Class type) { - if (context == null) { - try { - TestContextFactoryBean contextFactoryBean = delegate.getInstance(TestContextFactoryBean.class); - context = contextFactoryBean.getObject(); - initializeCitrus(context, contextFactoryBean.getApplicationContext()); - } catch (CucumberBackendException e) { - logger.warn("Failed to get proper TestContext from Cucumber Spring application context: " + e.getMessage()); - context = CitrusInstanceManager.getOrDefault().getCitrusContext().createTestContext(); - } - } - if (TestContext.class.isAssignableFrom(type)) { return (T) context; } @@ -101,14 +93,34 @@ public T getInstance(Class type) { return instance; } + /** + * Creates new test context for a test case. Uses the test context factory loaded by the Spring application context. + * Caches the test context factory to avoid initializing Citrus multiple times. + * Only refreshes the Citrus context in initialization when the Spring application context changes and therefore also the + * test context factory instance is different to the cached one. + * @return new test context instance created from the test context factory. + */ + private TestContext createTestContext() { + try { + TestContextFactoryBean testContextFactoryBean = delegate.getInstance(TestContextFactoryBean.class); + if (this.testContextFactory == null || !this.testContextFactory.equals(testContextFactoryBean)) { + this.testContextFactory = testContextFactoryBean; + initializeCitrus(testContextFactory.getApplicationContext()); + } + + return testContextFactory.getObject(); + } catch (CucumberBackendException e) { + logger.warn("Failed to get proper TestContext from Cucumber Spring application context: " + e.getMessage()); + return CitrusInstanceManager.getOrDefault().getCitrusContext().createTestContext(); + } + } + /** * Initialize new Citrus instance only if it has not been initialized before * or in case given application context is different to that one stored in the Citrus context. - * - * @param context * @param applicationContext */ - private void initializeCitrus(TestContext context, ApplicationContext applicationContext) { + private void initializeCitrus(ApplicationContext applicationContext) { if (CitrusInstanceManager.hasInstance()) { CitrusContext citrusContext = CitrusInstanceManager.getOrDefault().getCitrusContext();