I have an application that uses persistent JMS queues. Imagine I have an application failure after reading a message and before ack'ing it. The persistent queue must provide that message again after the app restarts. How can I implement a junit integration test for this? I'm testing application restart after a (simulated) application crash mid-"transaction".
I've looked at #DirtiesContext as a way to reset all the Spring parts of the app: reading configs, recreating JMS connections. I could have one test case A) write a message, allow the message to be read and then "exit" (shut down the spring context?) without acking. Then another test case (after the context is reloaded B) read the message and assert that it was not lost after the simulated application restart. But the builtin context reload provided by #DirtiesContext only happens between test cases. And JUnit does not provide for a means to sequence two test cases or make B) dependent on A), such that A) will always run (and run first) if you decide to run B).
In a previous life, I wrote manual code that shut down the spring context, and manually restarted a new context. E.g. between A) and B). That could be done within a single test case. It wouldn't play nicely with #RunWith(SpringRunner.class), I'm guessing, and seems pretty old school. Is that really the only option, given all the wonderful Spring and JUnit support these days?
This seems like a pretty useful technique. It could be used to test re-arrival of messages after they've been rolled back (and are stuck on a dead letter queue); or that sequence numbers written to a DB are really persisting during a "crash". Any number of failure cases that wind up affecting the next application startup due to persisted (or not) data. How do we simulate spring restart in junit tests? Either within one test, or create a sequence of dependent tests with #DirtiesContext between them.
The following article How to Restart a Spring Application Context within a JUnit test describes a solution to the question.
The solution consist to expend SpringJUnit4ClassRunner in order to inject a SpringRestarter singleton which takes care of restating the application context.
public class SpringRestarter {
private static SpringRestarter INSTANCE = null;
private TestContextManager testContextManager;
public static SpringRestarter getInstance() {
if (INSTANCE == null) {
INSTANCE = new SpringRestarter();
}
return INSTANCE;
}
public void init(TestContextManager testContextManager) {
this.testContextManager = testContextManager;
}
public void restart(Runnable stoppedLogic) {
testContextManager.getTestContext().markApplicationContextDirty(DirtiesContext.HierarchyMode.EXHAUSTIVE);
if (stoppedLogic != null) {
stoppedLogic.run();
}
testContextManager.getTestContext().getApplicationContext();
reinjectDependencies();
}
private void reinjectDependencies() {
testContextManager
.getTestExecutionListeners()
.stream()
.filter(listener -> listener instanceof DependencyInjectionTestExecutionListener)
.findFirst()
.ifPresent(listener -> {
try {
listener.prepareTestInstance(testContextManager.getTestContext());
} catch (Exception e) {
e.printStackTrace();
}
});
}
}
RestartingSpringJUnit4ClassRunner extends SpringJUnit4ClassRunner and initialise the singleton SpringRestarter.
public class RestartingSpringJUnit4ClassRunner extends SpringJUnit4ClassRunner {
public RestartingSpringJUnit4ClassRunner(Class<?> clazz) throws InitializationError {
super(clazz);
}
#Override
protected TestContextManager createTestContextManager(Class<?> clazz) {
final TestContextManager testContextManager = super.createTestContextManager(clazz);
SpringRestarter.getInstance().init(testContextManager);
return testContextManager;
}
}
RestartingSpringRunner extends RestartingSpringJUnit4ClassRunner in order to extend SpringRunner
public class RestartingSpringRunner extends RestartingSpringJUnit4ClassRunner {
public RestartingSpringRunner(Class<?> clazz) throws InitializationError {
super(clazz);
}
}
Using Within a JUnit 4 Test
#RunWith(RestartingSpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class MyIntegrationTests {
public void myRestartingTest() {
//Some test logic before the context restart
SpringRestarter.getInstance().restart(() -> {/* Some logic after context stopped */});
//Some test logic after the context restart
}
}
Full details of the explanation
Related
I need to test my Camel Exception handler:
#Configuration
public class RouteConfiguration extends RouteBuilder {
#Override
public void configure() throws Exception {
onException(HttpOperationFailedException.class).
handled(true).
log("HttpOperationFailedException: ${exception}").
onExceptionOccurred(myRestExceptionProcessor).id("myRestExceptionProcessor").end();
from("direct:myPrettyRoute").routeId("myPrettyRoute");//lots of routing here
}
}
I'm trying to add adviceWith after myRestExceptionProcessor, but can't find a way.
public class MyExceptionRoutingTest {
#Autowired
private CamelContext context;
#Before
public void before() throws Exception {
if (ServiceStatus.Stopped.equals(context.getStatus())) {
log.info("prepare mocks endpoint");
List<OnExceptionDefinition> ed = context.getErrorHandlerBuilder().getErrorHandlers(context.getRoutes().get(0).getRouteContext());
//FAILS, because context.getRoutes() is empty at the moment
//even if it wasn't, getErrorHandlerBuilder() is deprecated
}
}
}
I need to add something like this for the exceptionHandler definition:
.adviceWith(context, new AdviceWithRouteBuilder() {
#Override
public void configure() throws Exception {
weaveById("myExceptionProcessor").after().to(myResultEndpoint).id("myResponseEndpoint");
}
});
Is it possible?
I don't fully understand if you want to test your error handler (onException block) or just your myRestExceptionProcessor, but from a Camel perspective these are two kinds of tests:
Routing-Tests to test your routing logic and make sure that messages are correctly routed under various conditions that could happen in the route. This is the kind of tests you write with the Camel Testkit (that offers adviceWith and much more).
Classic unit tests to test an isolated Bean, Processor or anything else that is used in the route to implement business logic. This kind of test is done with JUnit, TestNG or other classic unit test frameworks, it has nothing to do with Camel. Do not try to test such components with Camel Route tests since it is much more complicated than in a unit test!
So, if you want to test your routing when an error occurs you throw the needed error in your route test to trigger the error handler. If you use a dependency injection framework like Spring this is easy since you can inject a test Bean that throws an error instead of a real Bean used in the route.
To add a Mock endpoint at the end of a route, use adviceWith
.adviceWith(camelContext, new AdviceWithRouteBuilder() {
#Override
public void configure() throws Exception {
weaveAddLast().to("mock:error");
}
}
Hope this helps a bit. Feel free to extend your question to elaborate your problem a bit more.
I've solved the trick as follows, without changing the route:
//entry point of the route is invoked here
Exchange send = myProducer.withBody("body is here").send();
HttpOperationFailedException exception = send.getException(HttpOperationFailedException.class);
String responseBody = exception.getResponseBody();
//recieved result and made assertions
assert responseBody != null; // any other assertions
The general problem I am trying to solve is this. I have a solution, but it's very clunky, and I'm hoping someone knows of a more orderly one.
Dropwizard offers a JUnit TestRule called DropwizardAppRule, which is used for integration tests. You use it like this:
#ClassRule
public static final DropWizardAppRule<MyConfiguration> APP_RULE = new DropwizardAppRule(MyApplication.class, myYmlResourceFilePath, ConfigOverride("mydatabase.url", myJdbcUrl));
It will start up your application, configuring it with your yml resource file, with overrides that you specified in the constructor. Note, however, that your overrides are bound at construction time.
There are also JUnit rules out there to start up a Docker container, and I'm using one to start up MySql, and a JUnit RuleChain to enforce the fact that the container must start up before I launch my Dropwizard application that depends on it.
All that works great, if I'm willing to specify in advance what port I want the MySql container to expose. I'm not. I want these integration tests to run on a build machine, quite possibly in parallel for branch builds of the same project, and I would strongly prefer to use the mechanism where you ask Docker to pick any available port, and use that.
The problem I run into with that, is that the exposed container port is not known at the time that the DropwizardAppRule is constructed, which is the only time you can bind configuration overrides.
The solution I adopted was to make a wrapper JUnit Rule, like so:
public class CreateWhenRunRuleWrapper<T extends ExternalResource> extends ExternalResource {
private final Supplier<T> wrappedRuleFactory;
private T wrappedRule;
public CreateWhenRunRuleWrapper(Supplier<T> wrappedRuleFactory) {
this.wrappedRuleFactory = wrappedRuleFactory;
}
public T getWrappedRule() {
return wrappedRule;
}
#Override
protected void before() throws Throwable {
wrappedRule = wrappedRuleFactory.get();
wrappedRule.before();
}
#Override
protected void after() {
wrappedRule.after();
}
}
This works, allowing me to construct the DropWizardAppRule class in the before() method, but is quite obviously outside JUnit's design intent, as shown by the fact that I had to locate it in the org.junit.rules package, in order to empower my class to be able to call the before() and after() methods of the late-created Rule.
What would be a more orderly, best practice way to accomplish the same objective?
2 Options we came up with:
The hacky solution is to use static {} which executes the code after spinning up the container but before initialising the dropwizard instance:
public static final GenericContainer mongodb = new GenericContainer("mongo:latest").withExposedPorts(27017);
static {
mongodb.start();
System.setProperty("dw.mongoConfig.uri", "mongodb://" + mongodb.getContainerIpAddress() + ":" + mongodb.getMappedPort(27017));
}
#ClassRule
public static final DropwizardIntegrationAppRule<Config> app1 = new DropwizardIntegrationAppRule<>(Service.class);
The second option is cleaner and much like yours.
private static final MongoDContainerRule mongo = new MongoDContainerRule();
private static final DropwizardIntegrationAppRule<Config> app = new DropwizardIntegrationAppRule<>(Service.class);
#ClassRule
public static final RuleChain chain = RuleChain
.outerRule(mongo)
.around(app)
MongoDContainerRule is like your wrapper but it also sets the right port through system properties.
public class MongoDContainerRule extends MongoDBContainerBase {
private static final GenericContainer mongodb = new GenericContainer("mongo:latest").withExposedPorts(27017);
#Override
protected void before() throws Throwable {
mongodb.start();
System.setProperty("dw.mongoConfig.uri", "mongodb://" + mongodb.getContainerIpAddress() + ":" + mongodb.getMappedPort(27017));
System.setProperty("dw.mongoConfig.tls", "false");
System.setProperty("dw.mongoConfig.dbName", DB_NAME);
}
#Override
protected void after() {
mongodb.stop();
}
}
The container will expose mongodb on a free port. mongodb.getMappedPort(internalPort) will return it. System.setProperty("dw.*") injects values into the dropwizard config.
According to the JUnit 5 User Guide, JUnit Jupiter provides backwards compatibility for some JUnit 4 Rules in order to assist with migration.
As stated above, JUnit Jupiter does not and will not support JUnit 4 rules natively. The JUnit team realizes, however, that many organizations, especially large ones, are likely to have large JUnit 4 codebases including custom rules. To serve these organizations and enable a gradual migration path the JUnit team has decided to support a selection of JUnit 4 rules verbatim within JUnit Jupiter.
The guide goes on to say that one of the rules is ExternalResource, which is a parent for TemporaryFolder.
However, the guide unfortunately doesn't go on to say what the migration path is, or what the equivalent is for those writing new JUnit 5 tests. So what should we use?
Interesting article by author of TemporaryFolderExtension for JUnit5
and
his code repo on github
JUnit5.0.0 is now in general release so let's hope they turn their attention to making the experimental stuff production-ready.
Meanwhile, it seems the TemporaryFolder rule will still work with JUnit5 docs
use this:
#EnableRuleMigrationSupport
public class MyJUnit5Test {
and this:
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-migrationsupport</artifactId>
<version>5.0.0</version>
</dependency>
As far as I understood, there can be no one to one mapping from ExternalResource to an equivalent in JUnit5. The concepts just don't fit. In JUnit4, the ExternalResource basically gives you a before and an after callback, but within the rule, you have no control about what before and after actually means. You could use it with #Rule or with #ClassRule.
In JUnit5, the extension is defined to hook in specific extension points and thus the 'when' is well defined.
Another difference in concepts would be, that you can have a state in JUnit4 rules, but your JUnit5 extensions shouldn't have any state. Instead, all state should go to the execution context.
Nevertheless, here is an option I came along with, where before and after relates to each test method:
public abstract class ExternalResourceExtension
implements BeforeTestExecutionCallback, AfterTestExecutionCallback {
#Override
public void beforeTestExecution(ExtensionContext context) throws Exception {
before(context);
}
#Override
public void afterTestExecution(ExtensionContext context) throws Exception {
after(context);
}
protected abstract void before(ExtensionContext context);
protected abstract void after(ExtensionContext context);
}
JUnit 5.4 comes with a built-in extension to handle temporary directories in tests.
#org.junit.jupiter.api.io.TempDir annotation can be used in order to annotate class field or a parameter in a lifecycle (e.g. #BeforeEach) or test method of type File or Path.
import org.junit.jupiter.api.io.TempDir;
#Test
void writesContentToFile(#TempDir Path tempDir) throws IOException {
// arrange
Path output = tempDir
.resolve("output.txt");
// act
fileWriter.writeTo(output.toString(), "test");
// assert
assertAll(
() -> assertTrue(Files.exists(output)),
() -> assertLinesMatch(List.of("test"), Files.readAllLines(output))
);
}
You can read more on this in my blog post, where you will find some more examples on utilizing this built-in extension: https://blog.codeleak.pl/2019/03/temporary-directories-in-junit-5-tests.html.
The documentation for that is still in the making - see pull request #660.
Temporary folders now have a solution in the way of #TempDir. However, what about the idea behind ExternalResources in general? Perhaps it's for a mock database, a mock HTTP connection, or some other custom resource you want to add support for?
The answer, it turns out is you can use the #RegisterExtension annotation to achieve something quite similar.
Example of use:
/**
* This is my resource shared across all tests
*/
#RegisterExtension
static final MyResourceExtension MY_RESOURCE = new MyResourceExtension();
/**
* This is my per test resource
*/
#RegisterExtension
final MyResourceExtension myResource = new MyResourceExtension();
#Test
void test() {
MY_RESOURCE.doStuff();
myResource.doStuff();
}
And here's the basic scaffolding of MyResourceExtension:
public class MyResourceExtension implements BeforeAllCallback, AfterAllCallback,
BeforeEachCallback, AfterEachCallback {
private SomeResource someResource;
private int referenceCount;
#Override
public void beforeAll(ExtensionContext context) throws Exception {
beforeEach(context);
}
#Override
public void afterAll(ExtensionContext context) throws Exception {
afterEach(context);
}
#Override
public void beforeEach(ExtensionContext context) throws Exception {
if (++referenceCount == 1) {
// Do stuff in preparation
this.someResource = ...;
}
}
#Override
public void afterEach(ExtensionContext context) throws Exception {
if (--referenceCount == 0) {
// Do stuff to clean up
this.someResource.close();
this.someResource = null;
}
}
public void doStuff() {
return this.someResource.fooBar();
}
}
You could of course wrap this all up as an abstract class and have MyResourceExtension implement just protected void before() and protected void after() or some such, if that's your thing, but I'm omitting that for brevity.
I need to implement JBehave test running with next requirements:
1. If #BeforeStories method is failed - test stories should not be started.
For this I use Configuration
MostUsefulConfiguration().doSkipScenariosAfterFailure(false)
2. If one scenario in story is failed - next scenario should be started.
For this I use
MostUsefulConfiguration().doResetStateBeforeStory(false).doResetStateBeforeScenario(false)
These settings work independently, but if I use it together - doSkipScenariosAfterFailure(false) discontinue working
How to implement this requirements at the same time?
If story file contains GivenStories: full.story
then full.story still run even with doResetStateBeforeStory(false).doResetStateBeforeScenario(false)
I need to fix it also.
Test code:
#RunWith(JUnitReportingRunner.class)
public abstract class AbstractIntegrationTestStory extends JUnitStory {
#BeforeStories
public void beforeStories() throws ExecutionException {
assertTrue(false);
}
...
#Override
public Configuration configuration() {
return JBehaveTestHelper.configuration(this.getClass(), xref);
}
}
...
public class JBehaveTestHelper {
public static Configuration configuration(Class<? extends Embeddable> embeddableClass, CrossReference xref) {
return new MostUsefulConfiguration()
.doSkipScenariosAfterFailure(false)
.doResetStateBeforeStory(false)
.doResetStateBeforeScenario(false)
}
I need to integrate an existing spring application with a custom elasticsearch river, ES rivers manage their dependencies using Google Guice and run in their own set of threads.
I've created a simple class which returns a static reference to spring context and configured a Guice module that returns objects from spring context. To ensure proper synchronization across guice threads and spring ones I've used a CountDownLatch released after the context is fully initialized. Here is some code
public class GuiceSpringIntegrator implements ApplicationListener<ContextRefreshedEvent> {
private static ApplicationContext context;
private static final CountDownLatch contextLatch = new CountDownLatch(1);
#Override public void onApplicationEvent(ContextRefreshedEvent event) {
try {
// check some stuff in context
} finally {
log.debug("Setting application context as static field of {}", getClass().getSimpleName());
GuiceSpringIntegrator.context = event.getApplicationContext();
log.info("Releasing latch for application context");
contextLatch.countDown();
}
}
public static ApplicationContext getApplicationContext() {
if (null == context) {
log.info("ApplicationContext not yet initialized, wait for it in thread {}", Thread.currentThread().getName());
Uninterruptibles.awaitUninterruptibly(contextLatch); <-- !!! SPRING INITIALIZATION CODE HANGS HERE
log.debug("Returning application context since now context is initialized");
Preconditions.checkState(context != null, "ApplicationContext should have been initialized properly");
}
return context;
}
}
This is the guice module that uses the class above
/**
* Guice module configuration
*/
public class ElasticSearchModule extends AbstractModule {
#Override
protected void configure() {}
#Provides #Singleton TaskScheduler getSchedulerInstance() {
return GuiceSpringIntegrator.getApplicationContext().getBean(TaskScheduler.class);
}
// and so on...
}
However when I start the application (especially on fast servers) occasionally the application hangs at the line marked in the above code. I double checked every code path which lead to the #getApplicationContext() method call and they are (should be) invoked by guice, so eventually the latch should be released and the code should proceed.
Is there a better way to handle this case?
Is there a way to check if I'm inside spring initialization code, something like isEventDispatchThread for swing? I'd like to use that code to trace if I'm calling #getApplicationContext from spring initialization code in some way?
Why #onApplicationEvent seems to be invoked before the context initialization is actually completed?
Any hint on how to debug this issue? I was reading about taking thread dumps on live server, is that right?
This doesn't really answer your question except for point #4, but I want to write code so I'm making it into an answer instead of a comment.
I would try changing this:
Uninterruptibles.awaitUninterruptibly(contextLatch); <-- !!! SPRING INITIALIZATION CODE HANGS HERE
to this:
boolean awaitResult = Uninterruptibles.awaitUninterruptibly(contextLatch, 2, TimeUnit.MINUTES);
if(awaitResult) {
throw new IllegalStateException("Unable to load Spring Context");
}
Then you can catch that exception somewhere and write it's stack trace to a log; this won't fix your problem but it likely will give you some more visibility into where the deadlock is?