0

I am currently trying to learn consumer-driven contract testing in the context of Services that use ActiveMQ Messages to communicate. Spring offers a documentation for Spring Cloud Contract Verifier Messaging https://cloud.spring.io/spring-cloud-contract/spring-cloud-contract.html#_spring_cloud_contract_verifier_messaging but I was not able to follow it with the setting I have ( Spring Services and ActiveMQ ).

My question is:

  • Is it a good idea to use consumer-driven contract testing for messaging ?
  • What are the best practices ?
  • If its a good idea, do you have any good tutorials on this for consumer-driven contract testing spring services that communicate via JMS and ActiveMQ ?
Tommaso
  • 33
  • 5

2 Answers2

3

I managed to create a custom MessageVerifier for JMS and ActiveMQ which looks like this:

package de.itemis.seatreservationservice;

import com.fasterxml.jackson.databind.ObjectMapper;
import de.itemis.seatreservationservice.domain.ReservationRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.contract.verifier.messaging.MessageVerifier;
import org.springframework.context.annotation.Primary;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.messaging.support.GenericMessage;
import org.springframework.stereotype.Component;

import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.TextMessage;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Component
@Primary
public class JmsMessageVerifier implements MessageVerifier {

    @Autowired
    JmsTemplate jmsTemplate;

    @Autowired
    ObjectMapper mapper;

    @Override
    public void send(Object message, String destination) {
        jmsTemplate.convertAndSend(destination, message, new ReplyToProcessor());
    }

    @Override
    public Object receive(String destination, long timeout, TimeUnit timeUnit) {
        jmsTemplate.setReceiveTimeout(timeout);
        return receiveMessage(destination);
    }

    @Override
    public Object receive(String destination) {
        return receiveMessage(destination);
    }

    @Override
    public void send(Object payload, Map headers, String destination) {
        ReservationRequest request = null;
        try {
            request = mapper.readValue((String) payload, ReservationRequest.class);
        } catch (IOException e) {
            e.printStackTrace();
        }
        jmsTemplate.convertAndSend(destination, request, new ReplyToProcessor());
    }



    private Object receiveMessage(String queueName) {
        Message message = jmsTemplate.receive(queueName);
        TextMessage textMessage = (TextMessage) message;
        try {
            return new GenericMessage<>(textMessage.getText());
        } catch (JMSException e) {
            e.printStackTrace();
            return null;
        }
    }
}

Currently i need them in both test folders (producer and consumer). Normally i would expect that the JmsMessageVerifier in my Producer would be packaged inside the generated stubs JAR so that the consumer contract tests use this JmsMessageVerifier instead of implementing their own.

What are your thoughts in this Marcin Grzejszczak ?

I would create an Issue for that if its a useful feature.

Here is the repository with both services:

Tommaso
  • 33
  • 5
  • Thanks Tomasso! So you got the idea perfectly! From what I see you're using standard mechanism of sending and receiving the JMS messages. It means that you require the e.g. ActiveMQ to be running there in memory, right? I'm not saying that's wrong, but I wonder if we can somehow work this around (as we do with rabbitmq). BTW can you please file a PR to the master branch of Spring Cloud Contract and we can continue the discussion there? Also we're in progress of changing the docs so don't add them just yet ;) – Marcin Grzejszczak Jul 26 '19 at 08:40
  • 3
    Follow-up Github-Issue: https://github.com/spring-cloud/spring-cloud-contract/issues/1141 – Tommaso Jul 26 '19 at 09:00
0

but I was not able to follow it with the setting I have ( Spring Services and ActiveMQ ).

ActiveMQ is not supported out of the box, you would have to provide your own bean of MessageVerifier type, where you would teach the framework on how to send and receive messages

Is it a good idea to use consumer-driven contract testing for messaging ?

Absolutely! You can follow the same flow as with HTTP but for messaging

What are the best practices ?

It depends ;) You can follow the standard practices of doing cdc as if it was an http based communication. If you want to abstract the producer and the consumer of such messages in such a way that you care more about the topic / queue as such, you can follow this guideline https://cloud.spring.io/spring-cloud-static/Greenwich.SR2/single/spring-cloud.html#_how_can_i_define_messaging_contracts_per_topic_not_per_producer where we describe how to define messaging contracts per topic and not per producer

If its a good idea, do you have any good tutorials on this for consumer-driven contract testing spring services that communicate via JMS and ActiveMQ ?

As I said earlier we don't have such support out of the box. You can however use Spring Integration or Apache Camel and communicate with ActiveMQ via those.

Marcin Grzejszczak
  • 10,624
  • 1
  • 16
  • 32
  • Thank you for the fast reply. I implemented a custom MessageVerifier but now I am missing two other Dependencies which are injected into my generated tests: `@Inject ContractVerifierMessaging contractVerifierMessaging;` `@Inject ContractVerifierObjectMapper contractVerifierObjectMapper;` Is there an option to auto configure those two dependencies but still use a custom MessageVerifier? – Tommaso Jul 25 '19 at 09:42
  • Absolutely - it happens out of the box. Just check the `org.springframework.cloud.contract.verifier.messaging.noop.NoOpContractVerifierAutoConfiguration`. We define the missing beans. – Marcin Grzejszczak Jul 25 '19 at 09:58
  • I avoided this issue by declaring my JmsMessageVerifier as `@Primary` and keeping the `@AutoConfigureMessageVerifier` annotation – Tommaso Jul 25 '19 at 14:54
  • @MarcinGrzejszczak what is the sensible way to use spring cloud contract with ActiveMQ with the spring-jms setup that is currently in use for my services? Do I need to start using cloud stream/spring integration/apache camel instead of spring-jms? – KKS Jul 28 '19 at 22:27
  • You can use what you mentioned or the suggested solution in this answer. – Marcin Grzejszczak Jul 29 '19 at 05:37