@SpringBootTest не автоматически подключает JavaMailSender. Я все настроил, а также создал явный компонент. По-прежнему не работает

#java #spring #spring-boot #email

Вопрос:

Мне очень трудно понять, почему мой JavaMailSender экземпляр не создается, даже если я создал явный компонент. Среда IDE показывает мне успешную инъекцию, но все равно, когда я запускаю интеграционный тест и отлаживаю, значение javaMailSender этого всегда null зависит от того, что я делаю.

Вот мой тестовый класс с @Configuration классом, который явно создает JavaMailSender компонент.

 // MailServiceTest.java  @SpringBootTest class MailServiceTest {  @Autowired  @Qualifier("testJavaMailSender")  private JavaMailSender javaMailSender;  private final MailService mailService = new MailService(javaMailSender);  private final GreenMail greenMail = new GreenMail();    @BeforeEach  public void setup() {  greenMail.setUser("foo@localhost", "foo", "password");  greenMail.start();  }   @AfterEach  public void teardown() {  greenMail.stop();  }   @Test  public void shouldSendNotificationEmailToRecipient() {  final String expectedText = "An error occured while executing the Jira Connector import sync."    "Please see the log file: 'jira-connector.log' in the application directory";   mailService.notifyAdmin();   final MimeMessage[] receivedMessage = greenMail.getReceivedMessagesForDomain("my-ag.com");  final String actual = GreenMailUtil.getBody(receivedMessage[0]);   assertThat(actual).isEqualTo(expectedText);   } }  @Configuration class MailSenderTestConfiguration {   @Bean(name = "testJavaMailSender")  public JavaMailSender testJavaMailSender() {  JavaMailSenderImpl mailSender = new JavaMailSenderImpl();  mailSender.setHost("smtp.gmail.com");  mailSender.setPort(587);   mailSender.setUsername("my.gmail@gmail.com");  mailSender.setPassword("password");   Properties props = mailSender.getJavaMailProperties();  props.put("mail.transport.protocol", "smtp");  props.put("mail.smtp.auth", "true");  props.put("mail.smtp.starttls.enable", "true");  props.put("mail.debug", "true");   return mailSender;  } }  

Я поставил точку останова в первой строке shouldSendNotificationEmailToRecipient метода, и отладчик сообщает мне javaMailSender = null об этом . Есть какие-нибудь идеи? Я перепробовал все, что предлагали Google и Stackoverflow.

Комментарии:

1. Этого не может быть null , иначе ваш тест провалится с ошибкой, указывающей @Autowired , что он не может быть выполнен. Вы используете Junit убедитесь, что вы не смешиваете JUnit 4 и 5 в одном вызове.

2. первое предположение: MailSenderTestConfiguration улавливается сканированием компонентов? (если это так,) 2. угадайте: Есть javaMailSender == null или mailService.javaMailSender == null ??

Ответ №1:

Этот тест проходит (все в одном .файл java):

 @SpringBootTest class DemoApplicationTests {  @Autowired   @Qualifier("testJavaMailSender")  private JavaMailSender javaMailSender;   @Test  void contextLoads() {  assertNotNull(javaMailSender);  }  } @Configuration class MailSenderTestConfiguration {   @Bean(name = "testJavaMailSender")  public JavaMailSender testJavaMailSender() {  // for simplicity/brevity just:  return new JavaMailSenderImpl();  } }  

В то время как это не так (тот же файл, во втором утверждении):

 @SpringBootTest class DemoApplicationTests {  @Autowired // this happens "long after" instantiation/construction (of DemoApplicationTests object), but before @Test ;)  @Qualifier("testJavaMailSender")  private JavaMailSender javaMailSender;    // This will be FINALLY assigned, before DemoApplicationTests (object) is even (properly) created/before constructor.  final MailService ms = new MailService(javaMailSender);   @Test  void contextLoads() {  assertNotNull(javaMailSender); // pass!  assertNotNull(ms.ms); // FAILS here!  }  } @Configuration class MailSenderTestConfiguration {  @Bean(name = "testJavaMailSender")  public JavaMailSender testJavaMailSender() {  return new JavaMailSenderImpl();  } } class MailService {  final JavaMailSender ms;  MailService(JavaMailSender ms) {  this.ms = ms;  } }  

Проблема:

Инициализация MailService (в теле класса … т. е. «инициализатор экземпляра»)! (Это делается ДО создания объекта(т. е. «строительства»)/любой «весенней магии».)

Решение:

Инициализируйте MailService , когда JavaMailSender это доступно!

например, перейти @Autowired к конструктору (теста):

 @SpringBootTest class DemoApplicationTests {  final MailService ms;   public DemoApplicationTests(@Autowired @Qualifier("testJavaMailSender") JavaMailSender javaMailSender) {  ms = new MailService(javaMailSender);  }   @Test  void contextLoads() {  assertNotNull(ms.ms);  }  }  

И я бы даже предпочел:

 @SpringBootTest class DemoApplicationTests {  @Autowired  private MailService ms;  @Autowired  @Qualifier("testJavaMailSender")  private JavaMailSender javaMailSender;   @Test  void contextLoads() {  assertNotNull(javaMailSender);  assertSame(javaMailSender, ms.ms); //!  } }  @Configuration class MailSenderTestConfiguration {   @Bean(name = "testJavaMailSender")  public JavaMailSender testJavaMailSender() {  return new JavaMailSenderImpl();  }   @Bean  public MailService mailService(@Qualifier("testJavaMailSender") JavaMailSender javaMailSender) {  return new MailService(javaMailSender);  } }  

… «управиться» MailService к весне.

Ответ №2:

Конфигурация выглядит нормально. Я просто запускаю его на своем локальном компьютере, и он работает. Причина может заключаться в том, что ваша конфигурация MailSenderTestConfiguration не вызывается — попробуйте поместить точку останова в метод testJavaMailSender ().

Я бы попробовал поэкспериментировать с @SpringBootTest(classes = ....) и/или отдельным @TestConfiguration классом, чтобы убедиться, что тест загружает правильный контекст с моей конфигурацией теста.

Это мой тестовый класс (все в одном файле), который отлично работает:

 import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;  @SpringBootTest class ApplicationTests {   @Autowired  @Qualifier("testJavaMailSender")  private JavaMailSender javaMailSender;    @BeforeEach  public void setup() {  System.out.println("Do before each!");  }   @AfterEach  public void teardown() {  System.out.println("Do after each!");  }   @Test  public void shouldSendNotificationEmailToRecipient() {  System.out.println(javaMailSender.getMessage());   } }  @Configuration class MailSenderTestConfiguration {   @Bean(name = "testJavaMailSender")  public JavaMailSender testJavaMailSender() {  System.out.println("My config works!");  JavaMailSenderImpl mailSender = new JavaMailSenderImpl();  return mailSender;  } }  class JavaMailSenderImpl implements JavaMailSender {   @Override  public String getMessage() {  return "I'm the one you're looking for!";  } }  interface JavaMailSender {  String getMessage(); }  

Выход:

 My config works! ...... Do before each! I'm the one you're looking for! Do after each!