4

When I try to test a spring cloud stream function based method, it always happens NullPointerException about InputDestination.

I have two questions:

  1. It's hard for me to know how to write UT from the official doc. official test doc
  2. Besides, how to write integration Test if test file has some dependencies. It seems create a new context and always has NoSuchBeanDefination error.

I have tried as flow, but the context can not find some dependency beans.

@Test
public void sampleTest() {
    try (ConfigurableApplicationContext context = new SpringApplicationBuilder(
                TestChannelBinderConfiguration.getCompleteConfiguration(
                        MyTestConfiguration.class))
                .run("--spring.cloud.function.definition=uppercase")) {
        InputDestination source = context.getBean(InputDestination.class);
        OutputDestination target = context.getBean(OutputDestination.class);
        source.send(new GenericMessage<byte[]>("hello".getBytes()));
        assertThat(target.receive().getPayload()).isEqualTo("HELLO".getBytes());
    }
}

So I just want to write UT, but still have NPE.

Here is my code.


    @Bean
    public Function<Message<List<DemoBean>>, Message<DemoBean>> findFirstBean( ){
        return message -> {
            List<DemoBean> demoBeans =  message.getPayload();
            return MessageBuilder.withPayload(demoBeans.get( 0 )).build();
        };
    }

Here is my test.


    @SpringBootTest
    @ActiveProfiles(profiles = "local")
    @Import({ TestChannelBinderConfiguration.class})
    class FunctionDemoTest {

    @Autowired
    private InputDestination inputDestination;
    @Autowired
    private OutputDestination outputDestination;
    private FunctionDemo functionDemo;
    // some dependency need to mock
    private DemoService demoService;


    @BeforeEach
    void setUp() {
        demoService = Mockito.mock( DemoService.class );
        functionDemo = new FunctionDemo( demoService);
    }


    @Test
    public void findFirstBeanTest() {
        DemoBean demoBean = new DemoBean();
        demoBean.setName("Howard");
        demoBean.setAge( 1 );
        DemoBean demoBean1 = new DemoBean();
        demoBean1.setName("Frank");
        demoBean1.setAge( 2 );
        List<DemoBean> demoBeanList = new ArrayList<>();
        demoBeanList.add( demoBean );
        demoBeanList.add( demoBean1 );

        Message<List<DemoBean>> inputMessage = MessageBuilder.withPayload(demoBeanList).build();
        inputDestination.send(inputMessage,"findFirstBean-in-0");
        
        Assertions.assertNotNull(  outputDestination.receive( 10000, "findFirstBean-out-0") );

    }


    }

Here is error:

java.lang.NullPointerException: while trying to invoke the method org.springframework.messaging.SubscribableChannel.send(org.springframework.messaging.Message) of a null object returned from org.springframework.cloud.stream.binder.test.InputDestination.getChannelByName(java.lang.String)

    at org.springframework.cloud.stream.binder.test.InputDestination.send(InputDestination.java:89)
    at com.successfactors.caf.listener.FunctionDemoTest.raePdrResultProcessor(FunctionDemoTest.java:82)
Frank
  • 83
  • 8

1 Answers1

1

Well, I know the root cause of NPE.

Message<byte[]> receive(long timeout, String bindingName)

It seems should be destinationName instead of bindingName in source code.

Any other answers would be appreciated.

Frank
  • 83
  • 8
  • 1
    Yes, it should be destination name since test binder emulates an interaction with a real messaging system to ensure that the message gets a chance to go thru the same process as with real binder. So the Input/Output destination effectively emulate queues, topics etc of real messaging systems. These destination are bound to user code via abstraction called bindings. But yes you send/receive to/from destinations, not bindings – Oleg Zhurakousky May 05 '22 at 13:08
  • Yes, but the param name in source code is not appropriate. It may lead confusion. – Frank May 06 '22 at 02:01
  • I am not sure what do you mean `public void send(Message> message, String destinationName)`. Also, the javadoc states `Allows the {@link Message} to be sent to a Binder's destination.. . .` and then there is even clarification. What else do you think is missing? – Oleg Zhurakousky May 06 '22 at 09:42
  • I mean the variable name is not correct in source code. It seems should be destinationName instead of bindingName when calling `Message receive(long timeout, String bindingName)`. – Frank Aug 18 '22 at 09:06