Contract testing messages in a Flux in Spring Cloud Stream gives IllegalArgumentException Message must not be null

huangapple 未分类评论66阅读模式
标题翻译

Contract testing messages in a Flux in Spring Cloud Stream gives IllegalArgumentException Message must not be null

问题

我正在尝试使用Spring Cloud Stream和Spring Cloud Contract对从端点发出的事件流进行契约测试。但是,当我运行我的测试时,我得到一个IllegalArgumentException,详细信息是Message must not be null。换句话说,我在我的消息接收器上没有收到任何消息。我的Spring Cloud版本是Hoxton。我在我的pom中添加了以下依赖项:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-stream-binder-kafka</artifactId>
</dependency>
<!--TEST -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-stream-test-support</artifactId>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-contract-verifier</artifactId>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-stream</artifactId>
    <type>test-jar</type>
    <scope>test</scope>
    <classifier>test-binder</classifier>
</dependency>

我已经按照这里的示例配置了一个新的绑定,链接在此。当我运行应用程序并发送curl -X POST localhost:8080请求时,它可以正常工作。

application.properties文件:

stubrunner.stream.enabled=true
spring.cloud.stream.bindings.uppercase-in-0.destination=test
spring.cloud.stream.bindings.uppercase-in-0.group=testGroup
spring.cloud.stream.bindings.uppercase-out-0.destination=hood
spring.cloud.stream.bindings.consume-in-0.destination=hood
spring.cloud.stream.bindings.consume-out-0.destination=downtown
spring.cloud.stream.bindings.supplier-out-0.destination=test
spring.cloud.function.definition=uppercase;consume;supplier

控制器:

@PostMapping(value = "/")
public void handlePost() {
    Faker faker = new Faker();
    Message<String> message = MessageBuilder.withPayload(faker.chuckNorris().fact()).build();
    EventSupplier.processor.onNext(message);
}

Bean定义:

@Configuration
public class EventSupplier {
    public static final EmitterProcessor<Message<String>> processor = EmitterProcessor.create();

    @Bean
    public Supplier<Flux<Message<String>>> supplier() {
        return () -> processor;
    }
}

合同基础测试类:

@ActiveProfiles("test")
@SpringBootTest
@RunWith(SpringRunner.class)
@AutoConfigureMessageVerifier
@EnableBinding(KafkaStreamerApplication.class)
public abstract class KafkaStreamerApplicationTests {
    @Autowired
    private DummyController dummyController;

    public void supply() {
        dummyController.handlePost();
    }
}

契约:

org.springframework.cloud.contract.spec.Contract.make {
    label 'some_label'
    input {
        triggeredBy("supply()")
    }
    outputMessage {
        sentTo('supplier-out-0')
        body(
            anyNonBlankString()
        )
        headers {
            messagingContentType(applicationJson())
        }
    }
}

我尝试过创建函数和消费者的合同,两者都有效。我还尝试过使用类型为String的供应商,而不是Flux<Message<String>>,也可以工作。你可以查看上面代码中的注释行。

在调试过程中,我发现在StreamMessageCollectorMessageReceiver中,在检索MessageChannel bean时,当调用receive("supplier-out-0", 5, "SECONDS")方法时,我得到的是一个DirectWithAttributesChannel实例。此外,在查看文档时,我发现了一个MessageChannel的实现,对我来说,它看起来是应该被实例化的候选对象 FluxMessageChannel

我猜想,Spring Cloud Contract正在等待将消息直接发送到队列中(DirectWithAttributesChannel),但由于Flux已经在队列中,我只是更新了Flux的内容,这意味着没有消息被推送到队列中(FluxMessageChannel?),因此没有消息被接收到(null),这就是为什么在运行测试时会出现IllegalArgumentException和详细错误信息Message must not be null的原因。

我的配置中是否缺少什么?这种情况是否可以通过合同进行测试?使用合同来测试这种情况是否是一种不好的方法?

英文翻译

I'm trying to contract test a Flux of events emitted from an endpoint using Spring Cloud Stream and Spring Cloud Contract. However, I'm getting an IllegalArgumentException with detailMessage Message must not be null when running my test. Or in other words I'm not receiving any messages on my message receiver. My spring cloud version is Hoxton. I've added the following dependencies in my pom

    &lt;dependency&gt;
		&lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
		&lt;artifactId&gt;spring-cloud-stream-binder-kafka&lt;/artifactId&gt;
	&lt;/dependency&gt;
    &lt;!--TEST --&gt;
	&lt;dependency&gt;
		&lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
		&lt;artifactId&gt;spring-cloud-stream-test-support&lt;/artifactId&gt;
		&lt;scope&gt;test&lt;/scope&gt;
	&lt;/dependency&gt;
	&lt;dependency&gt;
		&lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
		&lt;artifactId&gt;spring-cloud-starter-contract-verifier&lt;/artifactId&gt;
		&lt;scope&gt;test&lt;/scope&gt;
	&lt;/dependency&gt;
	&lt;dependency&gt;
		&lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
		&lt;artifactId&gt;spring-cloud-stream&lt;/artifactId&gt;
		&lt;type&gt;test-jar&lt;/type&gt;
		&lt;scope&gt;test&lt;/scope&gt;
		&lt;classifier&gt;test-binder&lt;/classifier&gt;
	&lt;/dependency&gt;

I've configured a new binding following the samples here. Is working properly when I run the app and I send a curl -X POST localhost:8080 request.

application.properties

stubrunner.stream.enabled=true
spring.cloud.stream.bindings.uppercase-in-0.destination=test
spring.cloud.stream.bindings.uppercase-in-0.group=testGroup
spring.cloud.stream.bindings.uppercase-out-0.destination=hood
spring.cloud.stream.bindings.consume-in-0.destination=hood
spring.cloud.stream.bindings.consume-out-0.destination=downtown
spring.cloud.stream.bindings.supplier-out-0.destination=test
spring.cloud.function.definition=uppercase;consume;supplier

Controller

 @PostMapping(value = &quot;/&quot;)
 public void handlePost() {
    Faker faker = new Faker();
    Message&lt;String&gt; message = MessageBuilder.withPayload(faker.chuckNorris().fact()).build();
    //    supplier.get();
    EventSupplier.processor.onNext(message);
 }

Bean definition

@Configuration
public class EventSupplier {
  public static final EmitterProcessor&lt;Message&lt;String&gt;&gt; processor = EmitterProcessor.create();

  @Bean
  //  public Supplier&lt;String&gt; supplier() {
  public Supplier&lt;Flux&lt;Message&lt;String&gt;&gt;&gt; supplier() {
    //    return () -&gt; &quot;&quot;;
    return () -&gt; processor;
  }

}

Contracts Base test

@ActiveProfiles(&quot;test&quot;)
@SpringBootTest
@RunWith(SpringRunner.class)
@AutoConfigureMessageVerifier
@EnableBinding(KafkaStreamerApplication.class)
public abstract class KafkaStreamerApplicationTests {

  @Autowired
  private DummyController dummyController;

  public void supply() {
     dummyController.handlePost();
  }
}

Contract

org.springframework.cloud.contract.spec.Contract.make {
  label &#39;some_label&#39;
  input {
   triggeredBy(&quot;supply()&quot;)
  }
  outputMessage {
    sentTo(&#39;supplier-out-0&#39;)
    body(
            anyNonBlankString()
            )
    headers {
        messagingContentType(applicationJson())
    }
  }
 }

I've tried creating contracts for function and consumer and both worked. I've also tried using a supplier of type String instead of Flux&lt;Message&lt;String&gt;&gt; and worked too. You can look at the commented lines at the code above.

While debugging I've seen that in StreamMessageCollectorMessageReceiver I'm getting a DirectWithAttributesChannel instance when retrieving the MessageChannel bean at receive(&quot;supplier-out-0&quot;, 5, &quot;SECONDS&quot;) method. Also while looking into the documentation I've found a MessageChannel implementation that, to me, looks like a candidate to be the one should have been instantiated FluxMessageChannel.

My guess is that spring cloud contract is waiting for a message to be sent into the queue directly(DirectWithAttributesChannel) but since the flux is already in the queue and I'm just updating the flux content, it means that not message is pushed into the queue(FluxMessageChannel?) so no message is received(null) and that's why I'm getting an IllegalArgumentException with details Message must not be null error when running my tests.

Is there something missing in my configuration? Can this scenario be tested by contracts? Is a bad approach to test this by using contracts?

huangapple
  • 本文由 发表于 2020年3月16日 17:20:18
  • 转载请务必保留本文链接:https://java.coder-hub.com/60703231.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定