Axon Testing: Missing Context - java

In an Axon-SpringBoot App I have an aggregate that uses an injected DAO in some of its command handlers.
For instance:
#Aggregate
class MyAggregate {
#CommandHandler
public MyAggregate (CreateMyAggregateCommand command, #Autowired MyAggregateDao dao) {
final SomeProperty = command.getSomePoprtery();
if (dao.findBySomeProperty(someProperty) == null) {
AggregateLifeCycle.apply(
new MyAggregateCreatedEvent(command.getIdentifier(),
someProperty);
} else {
// Don't create, already exits with some property
// report ...
}
}
}
A standard test like
#Test
void creationSucceeds () {
aggregateTestFixture = new AggregateTestFixture<>(MyAggregate.class);
final CreateMyAggregateCommand command = new CreateMyAggregateCommand(...);
final MyAggregateCreatedEvent = new MyAggregateCreatedEvent(...);
aggregateTestFixture
.givenNoPriorActivity()
.when(command)
.expectEvents(event);
}
fails with:
org.axonframework.test.FixtureExecutionException: No resource of type
[com.xmpl.MyAggregateDao] has been registered. It is required
for one of the handlers being executed.
How can I provide a test implementation?

Solution 1: Mocking
Since this is about unit testing and my question involves database calls (external service), mocking seems applicable as long as testing is about isolated aggregate behavior only.
#Test
void creationSucceeds () {
aggregateTestFixture = new AggregateTestFixture<>(MyAggregate.class);
aggregateTestFixture.registerInjectableResource(
Mockito.mock(MyAggregateDao.class));
}
Solution 2: Real Injection (jUnit 5)
This one works for me:
Get small library for Spring jUnit5 testing support from this github repo
Annotate test classes:
#SpringBootTest(classes = {SpringTestConfig.class})
#ExtendWith(SpringExtension.class)
public class MyAggregateTest {
// ...
}
Place application.properties in src/test/resources
Write Spring Test Configuration which starts a fully functioning Spring container:
#EnableAutoConfiguration
public class SpringTestConfig {
// Set up whatever you need
#Bean
#Autowired
MyAggregateDao myDao (DataSource dataSource) {
// ...
}
#Bean
#Autowired
EventStorageEngine eventStorageEngine () {
return new InMemoryEventStorageEngine();
}
}
Inject directly into your tests, configure AggregateTestFixture
private AggregateTestFixture<MyAggregate> aggregateTestFixture;
#Autowired
private MyAggregateDao myDao;
#BeforeEach
void beforeEach () {
aggregateTestFixture = new AggregateTestFixture<>(MyAggregate.class);
// We still need to register resources manually
aggregateTestFixture.registerInjectableResource(myDao);
}
With jUnit 4
Setting up a test configuration that starts a Spring container with jUnit 4 is a bit different but there's enough documentation out there. Start here

Related

Using Spring Boot #MockBean annotation in RabbitMQ Listener

I have a base class for integration tests which is setting up a Testcontainer for RabbitMQ. I'm inheriting in every Integration Test from this particular class and it works really well for me.
However, I do have a test which requires a mock instance as a bean. When the test runs alone everything works well, but if any integration test runs before this test, a mocked bean is not injected into the RabbitMQ Listener instance bean but the implementation is used.
Which options do I have to register a mocked instance in the application context?
I have tried so far:
#DirtiesContext
only on the test class doesn't work
on both test classes works
#TestConfiguration with #Primary Bean
The code is also available on GitHub: https://github.com/twobiers/mockbean-rabbitmq
This is my RabbitMQListener which makes use of a bean:
#Component
public class MessageConsumer {
private final BeanToConsume bean;
public MessageConsumer(BeanToConsume bean) {
this.bean = bean;
}
#RabbitListener(bindings = {
#QueueBinding(
value = #Queue(value = "test", durable = "true"),
exchange = #Exchange(value = "amq.fanout", type = "fanout")
)
})
public void getBean() {
System.out.println("BeanToConsume is of type: " + bean.getClass().getName());
}
}
My Base class:
#SpringBootTest
class AbstractIntegrationTest {
static RabbitMQContainer rabbitMQ = new RabbitMQContainer("rabbitmq:3.9.5-alpine");
static {
rabbitMQ.start();
}
#DynamicPropertySource
static void setRabbitMQ(DynamicPropertyRegistry registry) {
registry.add("spring.rabbitmq.host", () -> "localhost");
registry.add("spring.rabbitmq.port", () -> rabbitMQ.getAmqpPort());
registry.add("spring.rabbitmq.username", () -> rabbitMQ.getAdminUsername());
registry.add("spring.rabbitmq.password", () -> rabbitMQ.getAdminPassword());
}
}
And both test classes
// This runs first
class FirstIntegrationTest extends AbstractIntegrationTest {
#Autowired
RabbitTemplate rabbitTemplate;
#Test
void test() {
rabbitTemplate.convertAndSend("amq.fanout", "", "test");
}
}
// Afterwards this
class SecondIntegrationTest extends AbstractIntegrationTest {
#MockBean
BeanToConsume beanToConsume;
#Autowired
RabbitTemplate rabbitTemplate;
#Test
void test() {
rabbitTemplate.convertAndSend("amq.fanout", "", "test");
}
}
You use the same RabbitTemplate bean in two tests, and in one of the test classes, you inject a seemingly unrelated mock bean and expect the RabbitTemplate to be reinitialized. That’s not how it works, because Spring beans are singleton scoped by default, and since the RabbitTemplate is already created in FirstIntegrationTest without a mocked dependency, the same instance in injected again into SecondIntegrationTest.
I didn’t try to run your GitHub code, but can offer two choices off the top of my head.
Create the mock bean in the abstract superclass. If you never need the real bean, this is the simplest option.
Create the RabbitTemplate with prototype scope in SecondIntegrationTest. This will allow for the bean to be mocked only in this class, while other tests can use the real one.

Test spring configuration with ConditionalOnCloudPlatform annotation

I am trying to test my Spring configuration class which is annotated with ConditionalOnCloudPlatform.
Here is a very simplified example of the configuration class (I can't post my actual code):
#Configuration
#ConditionalOnCloudPlatform(CloudPlatform.CLOUD_FOUNDRY)
public class CloudConfigurationExample {
#Bean
public MyBean myBean(MyProperties properties) {
return new MyBean(properties.getParam);
}
}
To test I was hoping to do this:
#RunWith(MockitoJUnitRunner.class)
public class CloudConfigurationExampleTest {
private CloudConfigurationExample cloudConfigurationExample;
private MyProperties myProperties;
#Before
public void setUp() {
myProperties = new MyProperies();
myProperties.setParam("test");
cloudConfigurationExample = new CloudConfigurationExample(myProperties);
}
#Test
public void test() {
MyBean myBean = cloudConfigurationExample.myBean();
// do asserts etc.
}
}
The issue I have is that ConditionalOnCloudPlatform is activated and expects a valid cloud connector to be present. As a result I get No suitable cloud connector found.
Does anyone know the correct way so get Junit to ignore this annotation? I tried setting an environment variable with VCAP_SERVICES, which is what this annotation expects, but it didn't work.
Thanks!
ConditionalOnCloudPlatform gets activated if environment contains properties VCAP_APPLICATION and VCAP_SERVICES
There are different ways to overcome this issue,
Firstly, ensure no properties containing above prefixes are passed.
Secondly, check for cloud profile #Profile("cloud") or ignore this class during test #Profile("!test") and many more ways.
code snippet:
CLOUD_FOUNDRY {
#Override
public boolean isActive(Environment environment) {
return environment.containsProperty("VCAP_APPLICATION")
|| environment.containsProperty("VCAP_SERVICES");
}
},

Injecting mock before Spring's post-construct phase

Basically, the question is in the title.
I faced a problem that in post-construct phase my bean (that is autowired in the bean that is going through post-construct phase right now) is already mocked, but all the behavior described by Mockito.when() doesn't work, all the calls return null.
While searching I found this solution.
But is it possible to make it work without using any 3rd party libraries?
Test class:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
#ContextConfiguration(classes = TestApplicationConfiguration.class)
public class ServiceTest {
#Autowired
#Qualifier("test")
private PCLPortType pclPortType;
#MockBean
private ClearingHelper сlearingHelper;
#MockBean
private OrganizationCacheRepository organizationCacheRepository;
#Before
public void setup() throws Exception{
OperationResultWithOrganizationSystemIdMappingList res = new OperationResultWithOrganizationSystemIdMappingList();
when(clearingHelper.getOrgIdSystemIdMapping(any(Keycloak.class))).thenReturn(res);
}
#Test
public void test() throws Exception{
pclPortType.call("123");
}
}
Test config:
#TestConfiguration
public class TestApplicationConfiguration {
#Bean(name = "test")
public PCLPortType pclPortTypeForTest() throws JAXBException {
...
}
#Bean
public Keycloak keycloak() {
return Mockito.mock(Keycloak.class);
}
}
Component where I want to get mocked beans:
#Component
public class OrganizationCacheJob {
private static final Logger logger =
LogManager.getLogger(OrganizationCacheJob.class);
private final ObjectFactory<Keycloak> factory;
private final ClearingHelper clearingHelper;
private final OrganizationCacheRepository organizationCacheRepository;
#Autowired
public OrganizationCacheJob(ObjectFactory<Keycloak> factory,
ClearingHelper clearingHelper,
OrganizationCacheRepository organizationCacheRepository) {
this.factory = factory;
this.clearingHelper = ClearingHelper;
this.organizationCacheRepository = organizationCacheRepository;
}
#PostConstruct
public void updateCacheRepository() {
doUpdateCacheRepository();
}
#Scheduled(cron = "${organization.cache.schedule}")
public void start() {
logger.info("Starting update organization cache.");
doUpdateCacheRepository();
logger.info("Job finished.");
}
private void doUpdateCacheRepository() {
try {
Keycloak keycloak = factory.getObject();
OperationResultWithOrganizationSystemIdMappingList orgIdSystemIdMapping = clearingHelper.getOrgIdSystemIdMapping(keycloak);
if (orgIdSystemIdMapping != null) {
orgIdSystemIdMapping.getContent().forEach(o -> organizationCacheRepository.saveOrgIdsSystemsIdsMappings(o.getOrgId(), o.getId()));
logger.debug("Was saved {} orgIds", orgIdSystemIdMapping.getContent().size());
}
} catch (Exception e) {
logger.error("Error fetching whole mapping for org and systems ids. Exception: {}", e);
}
}
}
So, in post-construct phase of OrganizationCacheJob I want to get res when calling clearingHelper, but instead I get null.
ClearingHelper is a regular Spring bean marked as a #Component with public methods.
Ahh ok I just realized - when you start your test case, whole env is up and running first, then you advance to testing phase. So, translating to your case - first you got injection and post-constructs called, then #Before method is done, thus the result.
So as you can see, code says more than all the words you could put in your original post.
If it is possible for you, use spies insteed of mocks. If it is not possible to construct that, you will have to redesign your tests to not rely on post construct.
In this case, since you want the same post-construct behavior for every test case, provide own factory method for given mock (like you did with keycloak) and move when-doReturn there. It will be guaranteed that it will happen before post construct.

Spring's #Retryable not working when running JUnit Test

I have this test:
#RunWith(MockitoJUnitRunner.class)
public class myServiceTest {
#InjectMocks
myService subject;
private myService spy;
#Before
public void before() {
spy = spy(subject);
}
#Test
public void testing() {
when(spy.print2()).thenThrow(new RuntimeException()).thenThrow(new RuntimeException()).thenReturn("completed");
spy.print1();
verify(spy, times(3)).print2();
}
and then I have:
#Service("myService")
public class myService extends myAbstractServiceClass {
public String print1() {
String temp = "";
temp = print2();
return temp;
}
#Retryable
public String print2() {
return "completed";
}
}
then I have this interface(which my abstractService implements):
public interface myServiceInterface {
#Retryable(maxAttempts = 3)
String print1() throws RuntimeException;
#Retryable(maxAttempts = 3)
String print2() throws RuntimeException;
}
but, I get a runtimeexception thrown when I run the test, leading me to believe it is not retrying. Am I doing this wrong?
This is because you are not using the SpringJUnitClassRunner.
Mockito and your own classes are not taking the #Retryable annotation in account. So you rely on the implementation of Spring to do so. But your test does not activate Spring.
This is from the SpringJUnit4ClassRunner JavaDoc:
SpringJUnit4ClassRunner is a custom extension of JUnit's BlockJUnit4ClassRunner which provides functionality of the Spring TestContext Framework to standard JUnit tests by means of the TestContextManager and associated support classes and annotations.
To use this class, simply annotate a JUnit 4 based test class with #RunWith(SpringJUnit4ClassRunner.class) or #RunWith(SpringRunner.class).
You should restructure your test class at least to something like:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes=MyConfig.class)
public class MyServiceTest {
#Configuration
#EnableRetry
#Import(myService.class)
public static class MyConfig {}
...
What am I doing there?
activate the Spring JUnit hook
specify the Spring context configuration class
define the spring configuration and import your service as a bean
enable the retryable annotation
Are there some other pitfalls?
Yes, you are using Mockito to simulate an exception. If you want to test this behaviour with Spring like this, you should have a look at Springockito Annotations.
But be aware of that: Springockito you will replace the spring bean completely which forces you to proxy the call of your retryable. You need a structure like: test -> retryableService -> exceptionThrowingBean. Then you can use Springockito or what ever you like e.g. ReflectionTestUtils to configure the exceptionThrowingBean with the behaviour you like.
You should reference the interface type of your service in your test: MyServiceInterface
And last but not least. There is a naming convention nearly all Java developers follow: class names have first letter of each internal word capitalized
Hope that helps.
Another way:
#EnableRetry
#RunWith(SpringRunner.class)
#SpringBootTest(classes={ServiceToTest.class})
public class RetryableTest {
#Autowired
private ServiceToTest serviceToTest;
#MockBean
private ComponentInsideTestClass componentInsideTestClass;
#Test
public void retryableTest(){
serviceToTest.method();
}
}
I think you should let Spring manage the bean, create the appropriate proxy and handle the process.
If you want to mock specific beans, you can create mocks and inject them to the service under test.
1st option could be unwrapping proxied service, creating mocks and manually injecting them:
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = {RetryConfiguration.class})
#DirtiesContext
public class TheServiceImplTest {
#Autowired
private TheService theService;
#Before
public void setUp(){
TheService serviceWithoutProxy = AopTestUtils.getUltimateTargetObject(theService);
RetryProperties mockRetryProperties = Mockito.mock(RetryProperties.class);
ReflectionTestUtils.setField(serviceWithoutProxy, "retryProperties", mockRetryProperties);
}
#Test
public void shouldFetch() {
Assert.assertNotNull(theService);
}
}
In this example, I mocked one bean, RetryProperties, and injected into the service. Also note that, in this approach you are modifying the test application context which is cached by Spring. This means that if you don't use #DirtiesContext, service will continue its way with mocked bean in other tests. You can read more here
Second option would be creating a test specific #Configuration and mock the depended bean there. Spring will pick up this new mocked bean instead of the original one:
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = {RetryConfiguration.class, TheServiceImplSecondTest.TestConfiguration.class})
public class TheServiceImplSecondTest {
#Autowired
private TheService theService;
#Test
public void shouldFetch() {
Assert.assertNotNull(theService);
}
#Configuration
static class TestConfiguration {
#Bean
public RetryProperties retryProperties() {
return Mockito.mock(RetryProperties.class);
}
}
}
In this example, we have defined a test specific configuration and added it to the #ContextConfiguration.

Elasticsearch Spring boot integration test

I am looking for the way to add embedded elasticsearch to my spring boot integration test.
I looked at elastic search integration test but it does not work together with spring boot as both should uses different test runner.
I have a class test as below unfortunately it does not work with error:
java.lang.IllegalStateException: No context information for thread:
Thread[id=1, name=main, state=RUNNABLE, group=main]. Is this thread
running under a class
com.carrotsearch.randomizedtesting.RandomizedRunner runner context?
Add #RunWith(class
com.carrotsearch.randomizedtesting.RandomizedRunner.class) to your
test class. Make sure your code accesses random contexts within
#BeforeClass and #AfterClass boundary (for example, static test class
initializers are not permitted to access random contexts).
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = App.class)
#WebAppConfiguration
#IntegrationTest("server.port:0")
public class TestExample extends ElasticsearchIntegrationTest {
TestRestTemplate testRestTemplate = new TestRestTemplate();
#Value("${local.server.port}")
int port;
#Test
public void testOne(){
ResponseEntity<String> results = testRestTemplate.getForEntity(String.format("http://localhost:%d/client/1", port), String.class);
System.out.print(results);
}
}
Does anybody has some ideas how to make them run or what is alternatives ??
You can actually do what you need without any additional elasticsearch testing dependencies. The idea is basically to create an embedded node and then use the NodeClient to communicate with it.
For that, I created my own EmbeddedElasticsearchServer class which looks (more or less) like this:
public class EmbeddedElasticsearchServer implements InitializingBean {
public EmbeddedElasticsearchServer() {
ImmutableSettings.Builder elasticsearchSettings = ImmutableSettings.settingsBuilder()
.put("http.enabled", "false")
.put("path.data", "target/elasticsearch-data");
node = nodeBuilder()
.local(true)
.settings(elasticsearchSettings.build())
.node();
client = node.client();
}
#Override
public void afterPropertiesSet() throws Exception {
// Initialization stuff:
// - create required indices
// - define mappings
// - populate with test data
}
public Client getClient() {
return client;
}
}
Then, in spring configuration (let's call it integration-test-context.xml) I did this:
<bean id="embeddedElasticsearchServer"
class="com.example.EmbeddedElasticsearchServer" />
<bean id="elasticsearchClient"
class="org.elasticsearch.client.node.NodeClient"
factory-bean="embeddedElasticsearchServer"
factory-method="getClient" />
Then you can just autowire the client in your test like this:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration("/integration-test-context.xml")
public abstract class AbstractElasticsearchIntegrationTest {
#Autowired
private Client elasticsearchClient;
// Your rests go here...
}

Categories