暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

使用Spring Cloud Contract做异步消息契约测试

铁花盆的小世界 2021-06-21
299

This recipe introduce an example which do async messaging contract test by Spring Cloud Contract. There are two microservices in this example, EquipmentActivityService
and EquipmentCmdService
. EquipmentActivityService
send EquipmentInfo
to EquipmentCmdService
by Spring Cloud Stream and Kafka. Below is class for event payload.

    @Data
    public class EquipmentInfo {
    private String equipmentId;
    private String equipmentSize;
    private List<RtiEvent> events;
    }


    @Data
    public class RtiEvent {
    private String operation;
    private String eventId;
    private String status;
    }

    In this example, EquipmentActivityService
    is messaging producer and EquipmentCmdService
    is messaging consummer. We can define an constract and do contract test for produer and consummer separately. If both    sides pass the contract test, then we think these two mircoservices can interact with async messaging successfully.

    Let us start to walk through the contract test on both sides step by step.


    Producer Side

    1. include spring-cloud-starter-contract-verifier
    dependency in pom.xml
      <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-contract-verifier</artifactId>
      <scope>test</scope>
      </dependency>
      2. include spring-cloud-contract-maven-plugin
      maven plugin in pom.xml

      You also need configure baseClassMappings
      to define Base Class for auto-created test class in produer side. You can configure here at first and implement the Base Class in the following step.

        <plugin>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-contract-maven-plugin</artifactId>
        <version>${spring-cloud-contract.version}</version>
        <extensions>true</extensions>
        <configuration>
        <baseClassMappings>
        <baseClassMapping>
        <contractPackageRegex>.*messaging.*</contractPackageRegex>
        <baseClassFQN>.rti.equipmentactivity.MessagingContractBase</baseClassFQN>
        </baseClassMapping>
        </baseClassMappings>
        </configuration>
        </plugin>
        3. write contract in groovy format

        You can create sub folder in $rootDir/src/test/resources/contracts
        , and then put the contract file into it.

          import org.springframework.cloud.contract.spec.Contract


          Contract.make {
          label 'triggerEquipmentInfoSendOutLabel' //1
          input {
          triggeredBy('triggerEquipmentInfoSendOut()') //2
          }
          outputMessage {
          sentTo( $(consumer('input'), producer('output')) ) //3
          body([ //4
          equipmentId : $(producer(regex('[A-Z]{4}[0-9]{6}'))),
          equipmentSize: "20GP",
          events : [[
          operation: 'EXPORT',
          eventId : $(producer(regex('[A-Z]{4}[0-9]{8}'))),
          status : 'OK'
          ], [
          operation: 'INPUT_PROCESSING',
          eventId : $(producer(regex('[A-Z]{4}[0-9]{8}'))),
          status : 'FAILED'
          ]
          ]
          ])
          headers {
          header('kafka_messageKey', $(producer(regex('[A-Z]{4}[0-9]{6}'))))
          messagingContentType(applicationJson())
          }
          }
          }

          Notes:

          1. contract label, which can be used to trigger sending event from stub when do test in consummer side.

          2. input trigger for produer side auto-created test codes, baseClassForTests need to implement it.

          3. For producer side, event will be sent to output
            channel. For consummer side, event will come from input
            channel.

          4. event payload body.  Field value can use String or Regex for match.


          4. implement MessagingContractBase
          base class for tests
            package com.yyssky.rti.equipmentactivity;


            @RunWith(SpringRunner.class)
            @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
            @AutoConfigureMessageVerifier
            public abstract class MessagingContractBase {


            //@Autowired
            //private MessageVerifier verifier;


            public void triggerEquipmentInfoSendOut() {
            }


            }

            run mvn clean install
            , test will be failed since triggerEquipmentInfoSendOut() do nothing, codes for messaging outgoing is not  implemented.


            5. implement messaging outgoing codes

            You need to add a class and method in it to send out event as below.

              @Service
              @EnableBinding({Processor.class})
              public class EquipmentActivityService {


              @Autowired
              private Processor processor;


              public void sendEquipmentInfo() {


              RtiEvent event1 = RtiEvent.builder().eventId("ASDF12345678").operation("EXPORT").status("OK").build();
              RtiEvent event2 = RtiEvent.builder().eventId("QWER09876543").operation("INPUT_PROCESSING").status("FAILED").build();
              List<RtiEvent> list = new ArrayList<>();
              list.add(event1);
              list.add(event2);
              EquipmentInfo equipmentInfo = EquipmentInfo.builder().equipmentId("ZXCV567890").equipmentSize("20GP").events(list).build();


              Message<EquipmentInfo> message = MessageBuilder
              .withPayload(equipmentInfo)
              .setHeader(KafkaHeaders.MESSAGE_KEY, equipmentInfo.getEquipmentId())
              .build();


              processor.output().send(message);


              }
              }

              Then you need to call the method in triggerEquipmentInfoSendOut() in the base test class.

                package com.yyssky.rti.equipmentactivity;


                @RunWith(SpringRunner.class)
                @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
                @AutoConfigureMessageVerifier
                public abstract class MessagingContractBase {


                @Autowired
                private EquipmentActivityService equipmentActivityService;


                public void triggerEquipmentInfoSendOut() {


                equipmentActivityService.sendEquipmentInfo();
                }


                }

                run mvn clean install
                again and the test should be past. You also can check stub jar is created and installed in local m2 repo.

                Now produer side has passed the contract test and also stub jar for consummer side is installed, we can start to do contract test in consummer side now.


                Consummer Side

                1. include spring-cloud-starter-contract-stub-runner
                dependency in pom.xml
                  <dependency>
                  <groupId>org.springframework.cloud</groupId>
                  <artifactId>spring-cloud-starter-contract-stub-runner</artifactId>
                  <scope>test</scope>
                  </dependency>
                  2. include Messaging Producer's stub dependency in pom.xml
                    <dependency>
                    <groupId>com.yyssky.rti</groupId>
                    <artifactId>equipment-activity</artifactId>
                    <classifier>stubs</classifier>
                    <version>0.0.1-SNAPSHOT</version>
                    <scope>test</scope>
                    <exclusions>
                    <exclusion>
                    <groupId>*</groupId>
                    <artifactId>*</artifactId>
                    </exclusion>
                    </exclusions>
                    </dependency>
                    3. write contract test class
                      @RunWith(SpringRunner.class)
                      @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
                      @AutoConfigureStubRunner(stubsMode = StubRunnerProperties.StubsMode.LOCAL, ids = "com.yyssky.rti:equipment-activity")
                      public class EquipmentCmdListenerTests {


                      @Autowired
                      private StubTrigger stubTrigger;


                      @Autowired
                      private EquipmentCmdListener equipmentCmdListener;


                      @Test
                      public void shouldReceiveEquipmentInfo() {


                      Assertions.assertThat(equipmentCmdListener.getReceivedEvents()).hasSize(0);
                      stubTrigger.trigger("triggerEquipmentInfoSendOutLabel");


                      Assertions.assertThat(equipmentCmdListener.getReceivedEvents()).hasSize(1);




                      }
                      }

                      In the test method, we use StubTrigger
                      to trigger stub send out event about the contract label.

                      run mvn clean test
                      it will be fail, since we have not implemented event listener in EquipmentCmdListener class. Let us do it.


                      4. implement EquipmentCmdListener
                      class to pass the test
                        @Component
                        @EnableBinding({Processor.class})
                        @Slf4j
                        public class EquipmentCmdListener {


                        private List<EquipmentInfo> receivedEvents = new ArrayList<>();


                        @StreamListener(Processor.INPUT)
                        public void onEvent(@Payload EquipmentInfo equipmentInfo){
                        log.info("************* receive event, equipmentInfo: {}", equipmentInfo.toString());


                        receivedEvents.add(equipmentInfo); //just for test
                        }


                        public List<EquipmentInfo> getReceivedEvents() {
                        return receivedEvents;
                        }




                        }

                        In the EquipmentCmdListener class, the List is just for test.

                        run mvn clean test
                        again, consummer side contract test will be pass.


                        END




                        文章转载自铁花盆的小世界,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                        评论