spring-cloud / spring-cloud-stream

Framework for building Event-Driven Microservices
http://cloud.spring.io/spring-cloud-stream
Apache License 2.0
998 stars 610 forks source link

Testing in 3.x? #1888

Closed brmakana closed 4 years ago

brmakana commented 4 years ago

Hello,

I'm trying to use 3.x of spring cloud streams and having trouble writing tests. I've written a simple Processor class that takes a Pojo and returns a Pojo, and would like to write a test similar to this one. However, org.springframework.cloud.stream.test is no longer around, so I'm having trouble figuring out how to write a 3.x version of this test.

The (pretty barebones) example in the docs,

@Test
public void sampleTest() {
    ApplicationContext context = new SpringApplicationBuilder(
                    TestChannelBinderConfiguration.class,
                    DemoTestBinderApplication.class)
                .web(WebApplicationType.NONE).run();
    InputDestination source = context.getBean(InputDestination.class);
    OutputDestination target = context.getBean(OutputDestination.class);
    source.send(new GenericMessage<byte[]>("hello".getBytes()));
    System.out.println("Result: " + new String(target.receive().getPayload()));
}

I also have a user-defined converter bean for my Person class:

public class PersonConverter extends AbstractMessageConverter {

    public PersonConverter() {
        super(new MimeType("application", "json"));
    }

    @Override
    protected boolean supports(final Class<?> clazz) {
        return Person.class.equals(clazz);
    }

    @Override
    protected Object convertFromInternal(final Message<?> message,
                                         final Class<?> targetClass,
                                         final Object conversionHint) {
     /// some code that does work, omitted from this post
    }
}

Shows getting results in the OutputDestination via target.receive().getPayload(); but, target.receive() returns a Message<byte[]>, instead of a Message<?>, so I'm not sure how to translate whatever byte[] spring is making into my pojo.

Here's what I have so far as my test:

@ExtendWith(SpringExtension.class)
@SpringBootTest
class ProcessorTest {

    @Autowired
    private InputDestination source;

    @Autowired
    private OutputDestination target;

    @Test
    public void sampleTest() throws Exception {
        final Person input = new Person();
        input.setFirstName("bob");
        source.send(new GenericMessage<Person>(input));
        // processor will uppercase first name
        // how to get the resulting Person from output? 
    }

    @TestConfiguration
    @Import(TestChannelBinderConfiguration.class)
    public static class TestConfig {

    }

and the processor:

@Component
@EnableBinding(Processor.class)
@Slf4j
public class PersonProcessor {

    @StreamListener(Processor.INPUT)
    @SendTo(Processor.OUTPUT)
    public Person process(@Payload final Person input) {
        if (input == null) {
            return null;
        }
       final Person result = new Person();
       result.setFirstName(input.getFirstName().toUpperCase());
        return result;
    }

The 2.x examples have a lot of helpers in the test package that no longer exists - for example, this test takes advantage of MessageQueueMatcher's receivesMessageThat and receivesPayloadThat methods.

What's the 'new' way of testing in 3.x?

olegz commented 4 years ago

@brmakana Given that this is a question I am going to close it. In the future please use our Stack Overflow channel. But given the amount of details you included I'll make an exception and answer it anyway.

In the following section of the user guide we talk about the concept of wire format. This is important when it comes to testing if you think about the context of what you are testing. If you want to test your converter then simply write a unit test and be done with it. However we provide the test binder for a different type of testing - to emulate a real binder and ensure that your components (code, extensions such as converters etc) behave in an identical way as they would with any other binder. (Our previous testing support was actually bypassing most of the binder code).Such behavior includes the same expectations for incoming and outgoing data and its format. Remember whether its Kafka or Rabbit or GCP, it always receives and sends raw bytes (i.e., byte[]). The fact that your function was invoked implies that the converter was invoked and that in itself is the validation that the converter worked in the context of spring-cloud-stream. That said, I should also assume that you have a separate unit test to test the actual converter and that such test has nothing to do with spring-cloud-stream.

Sure we can develop more utilities to help you properly interpret your data, but that is the question of priorities.

brmakana commented 4 years ago

Thanks @olegz for the reply and the helpful information - I had a suspicion that the intent of this kind of test was as you said, but wanted to make sure I wasn't missing anything in the 2.x->3.x upgrade.

Thanks again!