Annotation with parameters already filled in - java

In the tech stack I'm working with we have an existing Java annotation for our unit tests which gets used like
#TeamTest(size = TestSize.Medium, timeout = 5000)
void myTest() {
// setup and asserts and stuff
}
Is there a way to setup a separate annotation that would already have the size and timeout variables stored? Ex:
#TeamTest(size = TestSize.Medium, timeout = 5000)
public static #interface MediumTest {}
That way I could just do
#MediumTest
void myTest() {
// setup and asserts and stuff
}

Related

Micrometer Unit Test Java

I have created a Micrometer class where counters are created and incremented. How to write unit test cases for the public method and avoid registering or sending the events to micrometer.
public class MicroMeter {
private static final MeterRegistry registry = Metrics.globalRegistry;
private Counter createCounter(final String meterName, Map<String, String> mp) {
List<Tag> tags = new ArrayList<>();
for (Map.Entry<String, String> entry : mp.entrySet()) {
tags.add(Tag.of(entry.getKey(), entry.getValue()));
}
return Counter
.builder(meterName)
.tags(tags)
.register(registry);
}
private void incrementCounter(Counter counter) {
counter.increment();
}
public static void createCounterAndIncrement(final String meterName, Map<String, String> mp){
MicroMeter microMeter = new MicroMeter();
Counter counter = microMeter.createCounter(meterName, dimensions);
microMeter.incrementCounter(counter);
}
}
You can simply pass in an in-memory meter registry for unit testing. I don’t remember the class name, but Micrometer comes with one.
Your code needs to be designed to accept the registry, not create it.
Because the whole purpose of Micrometer is to integrate with your chosen backend (like Graphite), there aren’t a lot of benefits to be had purely from unit testing. Apart of just creating the metrics, you need to check that those are linearized if your backend doesn’t support tags, and other things like client-side histograms if those are enabled.
What I do myself and recommend is integration testing. Here are the general steps:
Create an in-memory meter registry; The registry should be a bean, not a static variable, and you can replace it for testing very easily.
Mock the sender for your backend, like GraphiteSender if I remember the name correctly, and use the mock to verify that metrics being sent.
One way of writing a test for this scenario is to utilize the SimpleMeterRegistry by adding it to the globalRegistry, fetch the Counter and then test the expected behaviour.
Example snippet:
private MeterRegistry meterRegistry;
#BeforeEach
void setUp() {
meterRegistry = new SimpleMeterRegistry();
Metrics.globalRegistry.add(meterRegistry);
}
#AfterEach
void tearDown() {
meterRegistry.clear();
Metrics.globalRegistry.clear();
}
#Test
void testCreateCounterAndIncrement() {
// When
MicroMeter.createCounterAndIncrement("meterName", Map.of("key", "val"));
// Then
var counter = meterRegistry.find("meterName").counter();
then(counter).isNotNull();
then(counter.count()).isEqualTo(1);
then(counter.getId().getTag("key")).isEqualTo("val");
}

How to override Quarkus #Retry.delay in a unit test?

I have a method:
#Retry(retryOn = SomeException.class, maxRetries = 5, delay = 180000, maxDuration = 360000)
public void checkIfSomethingIsReady() {
if (something != ready) {
throw new SomeException();
} else {
// do stuff
}
}
I'm trying to do some boundary testing on a method like this without having to wait or retry. Is there a way to override this configuration solely for tests?
Yes you can,
MicroProfile Fault Tolerance also allows configuration using
MicroProfile Config. For example:
com.example.MyService/hello/Retry/delay=5
For the following code example:
#Singleton
public class MyService {
#Retry(maxRetries = 10, delay = 180000, retryOn = IOException.class)
public String hello() {
...
}
}
Therefore it would be packagePath.ClassName/methodName/Retry/delay=yourNumber
For tests, just have a different properties file with a different value.
Official Documentation: https://download.eclipse.org/microprofile/microprofile-fault-tolerance-3.0/microprofile-fault-tolerance-spec-3.0.html#_config_fault_tolerance_parameters
https://smallrye.io/docs/smallrye-fault-tolerance/5.0.0/usage/basic.html#_configuration

Creating an annotation for JUnit 4/5 to initialize and inject an object in tests

I am developing a testing library for Kafka, Kafkaesque. The library lets you develop integration tests for Kafka using a fluid and elegant (?!) API. For now, I develop the version for Spring Kafka.
The library needs to be initialized in every test:
#Test
void consumeShouldConsumeMessagesProducesFromOutsideProducer() {
kafkaTemplate.sendDefault(1, "data1");
kafkaTemplate.sendDefault(2, "data2");
new SpringKafkaesque(broker)
.<Integer, String>consume()
.fromTopic(CONSUMER_TEST_TOPIC)
.waitingAtMost(1L, TimeUnit.SECONDS)
.waitingEmptyPolls(5, 100L, TimeUnit.MILLISECONDS)
.withDeserializers(new IntegerDeserializer(), new StringDeserializer())
.expecting()
.havingRecordsSize(2)
.assertingThatPayloads(Matchers.containsInAnyOrder("data1", "data2"))
.andCloseConsumer();
}
Instead of manually initializing the SpringKafkaesque object, I want to create an annotation that does the magic for me. Something like the #EmbeddedKafka annotation of Spring Kafka.
#SpringBootTest(classes = {TestConfiguration.class})
#Kafkaesque(
topics = {SpringKafkaesqueTest.CONSUMER_TEST_TOPIC, SpringKafkaesqueTest.PRODUCER_TEST_TOPIC})
class SpringKafkaesqueTest {
#Autowired
private Kafkaesque kafkaesque;
#Test
void consumeShouldConsumeMessagesProducesFromOutsideProducer() {
kafkaTemplate.sendDefault(1, "data1");
kafkaTemplate.sendDefault(2, "data2");
kafkaesque
.<Integer, String>consume()
.fromTopic(CONSUMER_TEST_TOPIC)
.waitingAtMost(1L, TimeUnit.SECONDS)
.waitingEmptyPolls(5, 100L, TimeUnit.MILLISECONDS)
.withDeserializers(new IntegerDeserializer(), new StringDeserializer())
.expecting()
.havingRecordsSize(2)
.assertingThatPayloads(Matchers.containsInAnyOrder("data1", "data2"))
.andCloseConsumer();
}
Is it possible? Any suggestion?
JUnit 4
One possible solution is to create a custom annotation processing using reflection. You can get the test method name with #Rule, so for example:
public class CustomAnnotationTest {
private SpringKafkaesque kafkaesqueInstance;
#Rule
public TestName testName = new TestName();
#Before
public void init() {
Method method = null;
try {
method = this.getClass().getMethod(testName.getMethodName());
} catch (Exception ex) {
// handle exceptions
}
if (method.isAnnotationPresent(EmbeddedKafka.class)) {
// Init your SpringKafkaesque instance here
// kafkaesqueInstance = new SpringKafkaesque(broker)
//
}
}
#EmbeddedKafka
#Test
public void testCustomAnnotated() {
// your test here
}
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
#interface EmbeddedKafka {
}
}
You need to store this instance in the class-level variable. For the methods with no #EmbeddedKafka annotation, this variable will be null.
JUnit 5
With JUnit 5 you may consider using parameter injection with ParameterResolver. First of all, you need to implement this interface:
public class KafkaesqueResolver implements ParameterResolver {
#Override
public boolean supportsParameter(ParameterContext parameterContext,
ExtensionContext extensionContext) throws ParameterResolutionException {
return parameterContext.getParameter().getType() == SpringKafkaesque.class;
}
#Override
public Object resolveParameter(ParameterContext parameterContext,
ExtensionContext extensionContext) throws ParameterResolutionException {
// Create an instance of SpringKafkaesque here and return it
return new SpringKafkaesque();
}
}
Next, add #ExtendWith(KafkaesqueResolver.class) annotation to your test class, and add a parameter to your test method, where you need the instance of SpringKafkaesque:
#ExtendWith(KafkaesqueResolver.class)
public class ParamInjectionTest {
#Test
public void testNoParams() {
// nothing to inject
}
#Test
public void testWithParam(SpringKafkaesque instance) {
// do what you need with your instance
}
}
No custom annotation required in this case.

Mocking a ListState in Apache Flink 1.4

I am writing some test code for a processElement function in Apache Flink 1.4:
public class ProcessFunctionClass {
public void processElement(Tuple2<String, String> tuple2, Context context, Collector<Tuple2<String, String>> collector) {
// if the state is empty, start a timer
if (listState.get().iterator().hasNext() == false)
context.timerService().registerEventTimeTimer(1000);
listState.add("someStringToBeStored");
// ...
}
}
public class ProcessFunctionClassTest {
private ProcessFunctionClass processFunctionClass;
#Mock
private ListState<String> listState;
#Before
public void setUp() throws Exception {
processFunctionClass = new ProcessFunctionClass();
}
#Test
public void testProcessElement() {
ListState mockListState = mock(ListState.class);
Iterable mockIterable = mock(Iterable.class);
Iterator mockIterator = mock(Iterator.class);
MockitoAnnotations.initMocks(this);
when(tDPListState.get()).thenReturn(mockIterable);
when(tDPListState.get().iterator()).thenReturn(mockIterator);
when(tDPListState.get().iterator().hasNext()).thenReturn(false);
processFunctionClass.processElement(tuple2, context, collector);
// verify(...)
}
}
When I debug using my IDE, just before I step into the processElement() method, listState is not null and appears to have been mocked successfully, but as soon as I get to listState.get().iterator().hasNext(), listState is null and I get a NullPointerException. What am I doing wrong here?
In ProcessFunctionClass you have a private listState variable.
In your test you create a completely unrelated mockListState variable and set some expectations on it.
For your test to work, you must provide a way (constructor or setter) to set ProcessFunctionClass.listState to desired value (your mocked list state)
On top of that, MockitoAnnotations.initMocks(this); seems to do nothing in your example: you haven't shown us any fields annotated with #Mock or #InjectMocks
Update
You are misusing #Mock annotation.
You should place it in the test class, not in class under test.
When placed in the test class, after a call to initMocks, the filed will be initialized with a mock of an appropriate type.
What you should fo instead:
remove MockitoAnnotations.initMocks(this);, you are creating all the mocks manually.
add a constructor in ProcessFunctionClass
public ProcessFunctionClass(ListState<String> listState) {
this.listState = listState
}
use this constructor in your test
var mockListState = mock(ListState.class);
var processFunctionClass = new ProcessFunctionClass();

Unit testing Amazon SWF child workflows

I have a parent workflow (ParentWorkflow) calling a child workflow (ChildWorkflow) and I'm trying to test out the call.
The parent code looks something like this:
public class ParentWorkflow {
private final ChildWorkflowClientFactory childWorkflowClientFactory =
new ChildWorkflowClientFactoryImpl();
public void runWorkflow() {
new TryCatch() {
#Override
protected void doTry() throws Throwable {
Promise<Void> workflowFinished = childWorkflowClient.childWorkflow(x);
...
}
...
}
}
I want to mock out the
childWorkflowClient.childWorkflow(x)
call, however when I am hooking up the unit test I don't appear to have the option to inject the client factory, the unit test code looks like this:
#Rule
public WorkflowTest workflowTest = new WorkflowTest();
#Mock
private Activities mockActivities;
private ParentWorkflowClientFactory workflowFactory
= new ParentWorkflowClientFactoryImpl();
#Before
public void setUp() throws Exception {
// set up mocks
initMocks(this);
workflowTest.addActivitiesImplementation(mockActivities);
workflowTest.addWorkflowImplementationType(ParentWorkflowImpl.class);
workflowTest.addWorkflowImplementationType(ChildWorkflowImpl.class);
I don't appear to be able to pass anything into the workflow implementation classes, is there another way I can mock the child workflow out?
You can test workflow code directly mocking its dependencies without using workflowTest:
/**
* Rule is still needed to initialize asynchronous framework.
*/
#Rule
public WorkflowTest workflowTest = new WorkflowTest();
#Mock
private ActivitiesClient mockActivities;
#Mock
private BWorkflowClientFactory workflowFactory;
#Before
public void setUp() throws Exception {
// set up mocks
initMocks(this);
}
#Test
public void myTest() {
AWorkflowImpl w = new AWorkflowImpl(workflowFactory);
w.execute(); // whatever execute method of the workflow
}
This approach allows testing parts of the workflow encapsulated in other objects instead of the entire workflow.
If for whatever reason (for example you are using other testing framework than JUnit) you don't want to rely on WorkflowTest #Rule asynchronous code can be always executed using AsyncScope:
#Test
public void asyncTest() {
AsyncScope scope = new AsyncScope() {
protected void doAsync() {
// Any asynchronous code
AWorkflowImpl w = new AWorkflowImpl(workflowFactory);
w.execute(); // whatever execute method of the workflow
}
};
scope.eventLoop();
}
EDIT: The below only applies to SpringWorkflowTest; WorkflowTest doesn't have addWorkflowImplementation for some reason.
The correct way to use the WorkflowTest would be to add a mock implementation for the child workflow rather than adding the actual type:
#Rule
public SpringWorkflowTest workflowTest = new SpringWorkflowTest();
#Mock
private Activities mockActivities;
#Mock
private ChildWorkflow childWorkflowMock;
private ParentWorkflowClientFactory workflowFactory
= new ParentWorkflowClientFactoryImpl();
#Before
public void setUp() throws Exception {
// set up mocks
initMocks(this);
workflowTest.addActivitiesImplementation(mockActivities);
workflowTest.addWorkflowImplementationType(ParentWorkflowImpl.class);
workflowTest.addWorkflowImplementation(childWorkflowMock);
...
}
The framework will then call this mock instead of the actual implementation when you use the factory.

Categories