All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.mats3.spring.test.MatsTestContext Maven / Gradle / Ivy

Go to download

Mats^3 Spring-specific testing tools, for quickly firing up a test-harness using either JUnit or Jupiter (JUnit 5).

The newest version!
package io.mats3.spring.test;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotatedBeanDefinitionReader;
import org.springframework.context.annotation.Import;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.annotation.DirtiesContext.ClassMode;
import org.springframework.test.context.ContextConfiguration;

import io.mats3.MatsFactory;
import io.mats3.MatsInitiator;
import io.mats3.spring.EnableMats;
import io.mats3.spring.test.MatsTestContext.MatsSimpleTestInfrastructureContextInitializer;
import io.mats3.test.MatsTestBrokerInterface;
import io.mats3.test.MatsTestLatch;
import io.mats3.test.broker.MatsTestBroker;
import io.mats3.util.MatsFuturizer;

/**
 * One-stop-shop for making simple Spring-based integration/unit tests of Mats endpoints (NOT utilizing SQL
 * Connections), automatically importing the configuration . The configuration is done in
 * {@link MatsTestInfrastructureConfiguration}. This annotation can be put on a test-class, or on a
 * {@literal @Configuration} class (typically a nested static class within the test-class).
 * 

* Observe: If this "kitchen sink" annotation doesn't serve your needs, an alternative for quickly putting a * {@link MatsFactory} into a Spring test context is to utilize the methods in {@link TestSpringMatsFactoryProvider} * within a {@literal @Bean} factory method. *

* The annotation, mostly via {@link MatsTestInfrastructureConfiguration}, sets up the following: *

    *
  • Meta-annotated with {@link DirtiesContext}, since the Spring Test Context caching system is problematic when one * is handling static/global resources like what an external Message Queue broker is. Read more below.
  • *
  • The {@link MatsTestInfrastructureConfiguration @MatsTestInfrastructureConfiguration} is Meta-annotated with * {@link EnableMats @EnableMats}, since you pretty obviously need that, and have better things to do than to write it * in each test.
  • *
* Beans that are put in the Spring test context: *
    *
  • A {@link MatsFactory} (making the {@literal @EnableMats} happy), created using * {@link TestSpringMatsFactoryProvider}.
  • *
  • The MatsFactory's {@link MatsFactory#getDefaultInitiator() default} {@link MatsInitiator} to * initiate messages with.
  • *
  • A {@link MatsTestBrokerInterface} that "hooks in" to the underlying MQ instance, providing (for now) DLQ * access.
  • *
  • A lazy-initialized {@link MatsFuturizer}, since this is often handy in tests.
  • *
  • A {@link MatsTestLatch} instance in the Spring Context, since when you need it, you need it both in the * {@literal @Configuration} class, and in the test-class - nice to already have an instance defined.
  • *
*

* For some more background on the Context Caching: * Read the Spring doc about Context Caching and read a "wont-fix" * bug report describing how the contexts aren't destroyed due to caching. Since we're setting up active message * queue consumers with their own threads on a (potentially) global Message Queue broker, the different contexts would * mess each other up: The old consumers would still consume messages that the new test setup expected its consumers to * take. For the in-memory broker case, a solution would be to set up a new message broker (with a new name) for each * context (each instantiation of a new Spring context), but this would not solve the situation where we ran the tests * against a process-external MQ (which {@link MatsTestBroker} supports, read its JavaDoc!). Had there been some * "deactivate"/"reactivate" events that could be hooked, we could have stopped the endpoints for the to-be-cached * Spring test context, before firing up a new Spring test context, and the restarted them when the context was picked * up and reused from the cache. Since such a solution does not seem to be viable, the only solution seems to be * dirtying of the context, thereby stopping it from being cached. * * @see TestSpringMatsFactoryProvider * @see MatsTestDbContext * @see MatsTestInfrastructureConfiguration * @see MatsTestInfrastructureDbConfiguration * @author Endre Stølsvik - 2016-06-23 / 2016-08-07 - http://endre.stolsvik.com * @author Endre Stølsvik - 2020-11 - http://endre.stolsvik.com */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) // @ContextConfiguration makes it possible to annotate the test class itself with this annotation @ContextConfiguration(initializers = MatsSimpleTestInfrastructureContextInitializer.class) // @Import makes it possible to annotate a @Configuration class with this annotation @Import(MatsTestInfrastructureConfiguration.class) // @DirtiesContext since most tests needs this. @DirtiesContext(classMode = ClassMode.AFTER_CLASS) // AFTER_CLASS is also default, just pointing it out. // @Documented is only for JavaDoc: The documentation will show that the class is annotated with this annotation. @Documented // Meta for @ActiveProfiles(MatsProfiles.PROFILE_MATS_TEST) @MatsTestProfile public @interface MatsTestContext { /** * The reason for this obscure way to add the {@link MatsTestInfrastructureConfiguration} (as opposed to just point * to it with "classes=..") is as follows: Spring's testing integration has this feature where any static * inner @Configuration class of the test class is automatically loaded. If we specify specify classes= or * location=, this default will be thwarted. * * @see * Context Configuration with Component Classes. */ class MatsSimpleTestInfrastructureContextInitializer implements ApplicationContextInitializer { // Use clogging, since that's what Spring does. private static final Log log = LogFactory.getLog(MatsSimpleTestInfrastructureContextInitializer.class); private static final String LOG_PREFIX = "#SPRINGMATS# "; @Override public void initialize(ConfigurableApplicationContext applicationContext) { log.debug(LOG_PREFIX + "Registering " + MatsTestInfrastructureConfiguration.class.getSimpleName() + " on: " + applicationContext); /* * Hopefully all the ConfigurableApplicationContexts presented here will also be a BeanDefinitionRegistry. * This at least holds for the default 'GenericApplicationContext'. */ new AnnotatedBeanDefinitionReader((BeanDefinitionRegistry) applicationContext.getBeanFactory()) .register(MatsTestInfrastructureConfiguration.class); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy