I have a CMS (Content management System) to store property key values.
public interface CMSConfig {
#Property(propertyName = "someConfig")
String getSomeConfig();
}
And the Configuration annotation code is
#Target({ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
public #interface Configuration {
String configName() default "";
}
And the way to call this CMS is
#Service
public class MockitoService {
#ManagedConfiguration
private CMSConfig cmsConfig;
public String method() {
return "Hello!" + cmsConfig.getSomeConfig();
}
}
ManagedConfiguration.java
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.FIELD, ElementType.METHOD})
public #interface ManagedConfiguration {
}
I am looking to do the UnitTest for CMSConfig
I am not able to Mock or create the bean of CMSConfig as it's an interface. I have tried multiple approaches.
My UnitTest case file is
#ExtendWith(MockitoExtension.class)
public class MockitoServiceTest {
#Autowired
MockitoService mockitoService;
#ManagedConfiguration
private CMSConfig cmsConfig;
#BeforeAll
public static void before() {
System.setProperty("cms.configs.dir", Paths.get("src", "main", "resources").toFile().getAbsolutePath());
}
#Test
public void testCMS(){
assertEquals(cmsConfig.getDummy(),"dummyvalueUatRes");
}
}
Please help if there's any way to do this.
My mockitoService and cmsConfig are null while running the test case.
Related
I've been desperately trying to build an extension that requires information from both the JUnit5 extension model and the Spring-Boot Test framework. Specifically, I'd like to hook into the ApplicationContext creation process using a ApplicationContextInitializer and a custom annotation:
#Retention(RUNTIME)
#Target(TYPE)
#ContextConfiguration(initializers = CustomContextInitializer.class)
public #interface CustomAnnotation {
String someOption();
}
The test then looks like this:
#SpringBootTest
#CustomAnnotation(someOption = "Hello There")
public class SomeTest {
...
}
Now, how do I access the CustomAnnotation instance of the test class from within my CustomContextInitializer?
class CustomContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
#Override
public void initialize(ConfigurableApplicationContext applicationContext) {
// How to access the Test Class here?
CustomAnnotation annotation = <Test Class>.getAnnotation(CustomAnnotation.class);
System.out.println(annotation.someOption());
}
}
Is it possible to somehow access the JUnit5 ExtensionContext during the creation of the ApplicationContext? It doesn't have to be from within a ApplicationContextInitializer. I just need a hook that is executed early enough so that I can inject some dynamically generated properties before the whole bean instantiation process actually starts.
Take a look at #DynamicPropertySource for injecting properties before the bean initialization. You can then use #RegisterExtension to register a custom extension that reads the annotation properties and makes them available through some method:
#CustomAnnotation(someOption = "Hello There")
public class SomeTest {
#RegisterExtension
static CustomExtension extension = new CustomExtension();
#DynamicPropertySource
static void registerProperties(DynamicPropertyRegistry registry) {
registry.add("property.you.need",
() -> customExtension.getProperty());
}
}
public class CustomExtension implements BeforeAllCallback {
private String property;
public String getProperty() {
return property;
}
#Override
public void beforeAll(ExtensionContext context) throws Exception {
CustomAnnotation annotation = context.getRequiredTestClass()
.getAnnotation(CustomAnnotation.class);
property = annotation.someOption();
}
}
I know it doesn’t answer the question about hooking JUnit 5 with Spring initialization mechanism, but if dynamic properties is all you need, this solves exactly that.
You can implement your own TestExecutionListener and use it to access annotation you mentioned
#Retention(RUNTIME)
#Target(ElementType.TYPE)
#TestExecutionListeners(listeners = CustomTestExecutionListener.class, mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS)
#interface CustomAnnotation {
String someOption();
}
static class CustomTestExecutionListener implements TestExecutionListener {
#Override
public void beforeTestClass(TestContext testContext) throws Exception {
final CustomAnnotation annotation = testContext.getTestClass().getAnnotation(CustomAnnotation.class);
System.out.println(annotation.someOption());
}
}
I have a test utility for with I need to have a fresh instance per test method (to prevent that state leaks between tests). So far, I was using the scope "prototype", but now I want to be able to wire the utility into another test utility, and the wired instances shall be the same per test.
This appears to be a standard problem, so I was wondering if there is a "test method" scope or something similar?
This is the structure of the test class and test utilities:
#RunWith(SpringRunner.class)
#SpringBootTest
public class MyTest {
#Autowired
private TestDriver driver;
#Autowired
private TestStateProvider state;
// ... state
// ... methods
}
#Component
#Scope("prototype") // not right because MyTest and TestStateProvider get separate instances
public class TestDriver {
// ...
}
#Component
public class TestStateProvider {
#Autowired
private TestDriver driver;
// ...
}
I'm aware that I could use #Scope("singleton") and #DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD) but this refreshes more than I need – a new TestDriver instance for each test would be enough. Also, this approach is error-prone because all tests using the TestDriver would need to know that they also need the #DirtiesContext annotation. So I'm looking for a better solution.
It is actually pretty easy to implement a testMethod scope:
public class TestMethodScope implements Scope {
public static final String NAME = "testMethod";
private Map<String, Object> scopedObjects = new HashMap<>();
private Map<String, Runnable> destructionCallbacks = new HashMap<>();
#Override
public Object get(String name, ObjectFactory<?> objectFactory) {
if (!scopedObjects.containsKey(name)) {
scopedObjects.put(name, objectFactory.getObject());
}
return scopedObjects.get(name);
}
#Override
public void registerDestructionCallback(String name, Runnable callback) {
destructionCallbacks.put(name, callback);
}
#Override
public Object remove(String name) {
throw new UnsupportedOperationException();
}
#Override
public String getConversationId() {
return null;
}
#Override
public Object resolveContextualObject(String key) {
return null;
}
public static class TestExecutionListener implements org.springframework.test.context.TestExecutionListener {
#Override
public void afterTestMethod(TestContext testContext) throws Exception {
ConfigurableApplicationContext applicationContext = (ConfigurableApplicationContext) testContext
.getApplicationContext();
TestMethodScope scope = (TestMethodScope) applicationContext.getBeanFactory().getRegisteredScope(NAME);
scope.destructionCallbacks.values().forEach(callback -> callback.run());
scope.destructionCallbacks.clear();
scope.scopedObjects.clear();
}
}
#Component
public static class ScopeRegistration implements BeanFactoryPostProcessor {
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory factory) throws BeansException {
factory.registerScope(NAME, new TestMethodScope());
}
}
}
Just register the test execution listener, and there will be one instance per test of all #Scope("testMethod") annotated types:
#RunWith(SpringRunner.class)
#SpringBootTest
#TestExecutionListeners(listeners = TestMethodScope.TestExecutionListener.class,
mergeMode = MergeMode.MERGE_WITH_DEFAULTS)
public class MyTest {
#Autowired
// ... types annotated with #Scope("testMethod")
}
I ran into the same problem some time ago and came to this solution:
Use Mocks
I wrote some methods to create specific mockito settings to add behavior to each mock.
So create a TestConfiguration class with following methods and bean definition.
private MockSettings createResetAfterMockSettings() {
return MockReset.withSettings(MockReset.AFTER);
}
private <T> T mockClass(Class<T> classToMock) {
return mock(classToMock, createResetAfterMockSettings());
}
and your bean definition will look like:
#Bean
public TestDriver testDriver() {
return mockClass(TestDriver .class);
}
MockReset.AFTER is used to reset the mock after the test method is run.
And finally add a TestExecutionListeners to your Test class:
#TestExecutionListeners({ResetMocksTestExecutionListener.class})
I have looked at every example on stack exchange and on spring's example website and everything seems like this should work. I must be missing something simple
I have a custom annotation that ideally I would like to apply on either all methods of a class if the class is annotated or on any method annotated. Here is the aspect, test, and code:
Annotation
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
#Target({METHOD})
#Retention(RUNTIME)
public #interface Monitor {
String value() default "Monitor";
}
Aspect
#Aspect
#Component
public class LatencyAspect {
#Autowired
private Logger logger;
#Around("#annotation(Monitor)")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
Object retVal = joinPoint.proceed();
logger.info("logged");
return retVal;
}
}
Test
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(loader = AnnotationConfigContextLoader.class,classes = {LatencyConfig.class, LatencyTest.ContextConfiguration.class})
#ComponentScan
public class LatencyTest {
final static Logger log = mock(Logger.class);
#Autowired
private SomeClass someClass;
#Test
public void testExample() throws Exception {
someClass.doSomething("foo");
verify(log).info("logged");
}
#EnableAspectJAutoProxy(proxyTargetClass = true)
#Configuration
static class ContextConfiguration {
#Bean
public SomeClass properties() {
return new SomeClass();
}
#Bean
public Logger log() {
return log;
}
}
public static class SomeClass {
#Monitor
#Transient
public String doSomething(String whatever) {
return "done";
}
}
}
Result
Wanted but not invoked:
logger.info("logged");
-> at org.bongiorno.latency.LatencyTest.testExample(LatencyTest.java:74)
Actually, there were zero interactions with this mock.
Links
Actual source
Spring doesn't pick up the #ComponentScan annotation from a JUnit test class. Move the annotation to your LatencyConfig class or the test-local LatencyTest.ContextConfiguration inner configuration class.
I have a test class :
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = IntegrationTestConfig.class)
#Transactional
#Component
public class AppTest {
#Autowired
private SimpleService simpleService;
#Test
public void test() {
simpleService.test();
}
}
I want make an aspect in order to monitor test method invocation
#Aspect
public class TestAspect {
#Pointcut("#annotation(org.junit.Test)")
public void pointcut() {
}
#After("pointcut()")
public void monitor() {
System.out.println("*** AFTER TEST ***");
}
}
Config :
#Configuration
#ImportResource("classpath:context.xml")
public class IntegrationTestConfig {
#Bean
public TestAspect testAspect() {
return new TestAspect();
}
}
But my monitor method has not been invoked, what's wrong? Generally - can I advice test methods?? I want to know which test method has been invoked.
JUnit itself has Rules that can be used for this.
For a simple POJO:
#Component
public class Foo
{
private final String string;
public Foo()
{
this("Secondary ComponentScan??");
}
public Foo(String string)
{
this.string = string;
}
#Override
public String toString()
{
return string;
}
}
and this configuration
#Configuration
#ComponentScan(basePackageClasses = Foo.class)
public class TestConfiguration
{
#Primary
#Bean
public Foo foo()
{
return new Foo("Primary bean!!");
}
}
I would expect the following test
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = TestConfiguration.class)
public class Test
{
#Autowired
private Foo foo;
#Test
public void test()
{
System.out.println(foo);
}
}
to print out Primary Bean!! but it returns Secondary ComponentScan?? instead...
How come? Nowhere does the documentation for #Primary say it fails against component-scanned beans!
The reason is that both beans actually have the same name foo, so internally one bean definition is getting overridden with the other one, essentially the one with #Bean is getting overridden by the one being scanned by #ComponentScan.
The fix is simply to give one of them a different name and you should see the correct behavior of the #Primary bean getting injected.
#Primary
#Bean
public Foo foo1()
{
return new Foo("Primary bean!!");
}
OR
#Component("foo1")
public class Foo
{
..