반응형
@SpringBoot 앱에서 RabbitListener 메서드 테스트
코드:
RabbitMQListener:
@Component
public class ServerThroughRabbitMQ implements ServerThroughAMQPBroker {
private static final AtomicLong ID_COUNTER=new AtomicLong();
private final long instanceId=ID_COUNTER.incrementAndGet();
@Autowired
public ServerThroughRabbitMQ( UserService userService,LoginService loginService....){
....
}
@Override
@RabbitListener(queues = "#{registerQueue.name}")
public String registerUserAndLogin(String json) {
.....
}
서버 구성:
@Configuration
public class ServerConfig {
@Value("${amqp.broker.exchange-name}")
private String exchangeName;
@Value("${amqp.broker.host}")
private String ampqBrokerHost;
@Value("${amqp.broker.quidco.queue.postfix}")
private String quidcoQueuePostfix;
@Value("${amqp.broker.quidco.queue.durability:true}")
private boolean quidcoQueueDurability;
@Value("${amqp.broker.quidco.queue.autodelete:false}")
private boolean quidcoQueueAutodelete;
private String registerAndLoginQuequName;
@PostConstruct
public void init() {
registerAndLoginQuequName = REGISTER_AND_LOGIN_ROUTING_KEY + quidcoQueuePostfix;
public String getRegisterAndLoginQueueName() {
return registerAndLoginQuequName;
}
public String getLoginAndCheckBonusQueueName() {
return loginAndCheckBonusQuequName;
}
@Bean
public ConnectionFactory connectionFactory() {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory(ampqBrokerHost);
return connectionFactory;
}
@Bean
public AmqpAdmin amqpAdmin() {
return new RabbitAdmin(connectionFactory());
}
@Bean
public TopicExchange topic() {
return new TopicExchange(exchangeName);
}
@Bean(name = "registerQueue")
public Queue registerQueue() {
return new Queue(registerAndLoginQuequName, quidcoQueueDurability, false, quidcoQueueAutodelete);
}
@Bean
public Binding bindingRegisterAndLogin() {
return BindingBuilder.bind(registerQueue()).to(topic()).with(REGISTER_AND_LOGIN_ROUTING_KEY);
}
}
테스트 구성:
@EnableRabbit
@TestPropertySource("classpath:test.properties")
public class ServerThroughAMQPBrokerRabbitMQIntegrationTestConfig {
private final ExecutorService=Executors.newCachedThreadPool();
private LoginService loginServiceMock=mock(LoginService.class);
private UserService userServiceMock =mock(UserService.class);
@Bean
public ExecutorService executor() {
return executorService;
}
@Bean
public LoginService getLoginServiceMock() {
return loginServiceMock;
}
@Bean
public UserService getUserService() {
return userServiceMock;
}
@Bean
@Autowired
public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory(ConnectionFactory connectionFactory) {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
factory.setMaxConcurrentConsumers(5);
return factory;
}
@Bean
@Autowired
public RabbitTemplate getRabbitTemplate(ConnectionFactory connectionFactory) {
final RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
return rabbitTemplate;
}
@Bean
public ServerThroughRabbitMQ getServerThroughRabbitMQ() {
return new ServerThroughRabbitMQ(userServiceMock, loginServiceMock,...);
}
}
통합 테스트:
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes ={ServerConfig.class,ServerThroughAMQPBrokerRabbitMQIntegrationTestConfig.class})
@Category({IntegrationTest.class})
@TestPropertySource("classpath:test.properties")
public class ServerThroughAMQPBrokerRabbitMQIntegrationTest {
final private ObjectMapper jackson = new ObjectMapper();
@Autowired
private ExecutorService executor;
@Autowired
private ServerThroughRabbitMQ serverThroughRabbitMQ;
@Autowired
private RabbitTemplate template;
@Autowired
private TopicExchange exchange;
@Autowired
UserService userService;
@Autowired
LoginService loginService;
@Autowired
private AmqpAdmin amqpAdmin;
@Autowired
private ServerConfig serverConfig;
final String username = "username";
final String email = "email@email.com";
final Integer tcVersion=1;
final int quidcoUserId = 1;
final String jwt = ProcessLauncherForJwtPhpBuilderUnitWithCxtTest.EXPECTED_JWT;
@Before
public void cleanAfterOthersForMyself() {
cleanTestQueues();
}
@After
public void cleanAfterMyselfForOthers() {
cleanTestQueues();
}
private void cleanTestQueues() {
amqpAdmin.purgeQueue(serverConfig.getRegisterAndLoginQueueName(), false);
}
@Test
@Category({SlowTest.class,IntegrationTest.class})
public void testRegistrationAndLogin() throws TimeoutException {
final Waiter waiter = new Waiter();
when(userService.register(anyString(), anyString(), anyString())).thenReturn(...);
when(loginService....()).thenReturn(...);
executor.submit(() -> {
final RegistrationRequest request = new RegistrationRequest(username, email,tcVersion);
final String response;
try {
//@todo: converter to convert RegistrationRequest inside next method to json
response = (String) template.convertSendAndReceive(exchange.getName(), REGISTER_AND_LOGIN_ROUTING_KEY.toString(), jackson.writeValueAsString(request));
waiter.assertThat(response, not(isEmptyString()));
final RegistrationResponse registrationResponse = jackson.readValue(response, RegistrationResponse.class);
waiter.assertThat(...);
waiter.assertThat(...);
} catch (Exception e) {
throw new RuntimeException(e);
}
waiter.resume();
});
waiter.await(5, TimeUnit.SECONDS);
}
}
테스트를 별도로 실행하면 모든 것이 정상적으로 작동하지만 다른 테스트와 함께 실행하면 조롱당한 ServerThroughRabbitMQ가 사용되지 않기 때문에 일부 스프링 캐시는 오래된 토끼 수신기를 사용하도록 강제합니다.
디버깅을 해보니 올바른 빈이 테스트에 자동 배선되고 있지만 어떤 이유로 오래된 수신기가 사용 중이고(오래된 빈 필드 인스턴스Id=1 new mailed bean instanceId=3) 테스트 실패(어떻게 가능한지 확실하지 않습니다. 기존의 오래된 빈의 경우 자동 배선 예외가 발생한다고 가정합니다.
@DirtiesContextBefore_CLASS를 사용하려고 했지만 다른 문제가 발생했습니다(여기 참조).
RabbitMQ는 일종의 상태를 유지하기 때문에 RabbitMQ 및 통합 테스트는 어려울 수 있습니다. - 대기열의 이전 테스트에서 메시지 - 이전 테스트에서 수신자가 대기열에서 계속 청취합니다.
몇 가지 접근 방식이 있습니다.
- 테스트를 시작하기 전에 모든 대기열을 정리합니다(이것이 의미할 수 있음).
cleanTestQueues()
) - 각 테스트 전에 모든 대기열 삭제(또는 임시 대기열 사용) 및 재생성
- Rabbit Admin Rest API를 사용하여 수신기 또는 이전 테스트의 연결 제거
- vhost를 삭제하고 각 테스트에 대한 인프라를 다시 생성합니다(가장 잔인한 방법).
언급URL : https://stackoverflow.com/questions/33994273/rabbitlistener-method-testing-in-springboot-app
반응형
'source' 카테고리의 다른 글
로컬 React 프론트엔드를 로컬 Spring Boot 미들웨어 응용 프로그램에 연결할 때 CORS 오류가 발생했습니다. (0) | 2023.07.21 |
---|---|
심층 학습 난 손실 이유 (0) | 2023.07.21 |
갑자기 멈추는 파이썬으로 거대한 CSV를 처리할 때 '킬드'는 무엇을 의미합니까? (0) | 2023.07.21 |
롬복 + 잭슨 불변량 (0) | 2023.07.21 |
math.log에서 ValueError: math domain error가 발생하는 이유는 무엇입니까? (0) | 2023.07.21 |