При создании приложения Spring Boot может возникнуть необходимость добавить конфигурацию почты. На самом деле, настройка почты в Spring Boot не сильно отличается от настройки в Spring Bootless приложении. Но как проверить, что конфигурация и отправка почты работают нормально? Давайте посмотрим.
Я предполагаю, что у нас есть простое загрузочное приложение Spring Boot. Если нет, то самый простой способ сделать это — использовать Spring Initializr .
Добавление зависимости javax.mail
Начнем с добавления зависимости javax.mail
в build.gradle
: compile 'javax.mail:mail:1.4.1'
. Нам также потребуется Spring Context Support
(если она отсутствует), которая содержит класс поддержки JavaMailSender
. Зависимость: compile("org.springframework:spring-context-support")
Конфигурация на основе Java
Spring Boot поддерживает конфигурацию на основе Java. Чтобы добавить конфигурацию почты, мы добавляем класс @Configuration
аннотацией @Configuration
. Свойства хранятся в mail.properties
(но это не обязательно). Значения свойств могут быть введены непосредственно в bean-компоненты с @Value
аннотации @Value
:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
@Configuration @PropertySource ( "classpath:mail.properties" ) public class MailConfiguration { @Value ( "${mail.protocol}" ) private String protocol; @Value ( "${mail.host}" ) private String host; @Value ( "${mail.port}" ) private int port; @Value ( "${mail.smtp.auth}" ) private boolean auth; @Value ( "${mail.smtp.starttls.enable}" ) private boolean starttls; @Value ( "${mail.from}" ) private String from; @Value ( "${mail.username}" ) private String username; @Value ( "${mail.password}" ) private String password; @Bean public JavaMailSender javaMailSender() { JavaMailSenderImpl mailSender = new JavaMailSenderImpl(); Properties mailProperties = new Properties(); mailProperties.put( "mail.smtp.auth" , auth); mailProperties.put( "mail.smtp.starttls.enable" , starttls); mailSender.setJavaMailProperties(mailProperties); mailSender.setHost(host); mailSender.setPort(port); mailSender.setProtocol(protocol); mailSender.setUsername(username); mailSender.setPassword(password); return mailSender; } } |
Аннотация @PropertySource
делает mail.properties
доступным для внедрения с помощью @Value
. аннотаций. Если это не сделано, вы можете ожидать исключения: java.lang.IllegalArgumentException: Could not resolve placeholder '<name>' in string value "${<name>}"
.
И mail.properties
:
1
2
3
4
5
6
7
8
|
mail.protocol=smtp mail.host=localhost mail.port= 25 mail.smtp.auth= false mail.smtp.starttls.enable= false mail.from=me @localhost mail.username= mail.password= |
Конечная точка почты
Чтобы иметь возможность отправить электронное письмо в нашем приложении, мы можем создать конечную точку REST. Мы можем использовать SimpleMailMessage
в Spring для быстрой реализации этой конечной точки. Давайте посмотрим:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
@RestController class MailSubmissionController { private final JavaMailSender javaMailSender; @Autowired MailSubmissionController(JavaMailSender javaMailSender) { this .javaMailSender = javaMailSender; } @RequestMapping ( "/mail" ) @ResponseStatus (HttpStatus.CREATED) SimpleMailMessage send() { SimpleMailMessage mailMessage = new SimpleMailMessage(); mailMessage.setTo( "someone@localhost" ); mailMessage.setReplyTo( "someone@localhost" ); mailMessage.setFrom( "someone@localhost" ); mailMessage.setSubject( "Lorem ipsum" ); mailMessage.setText( "Lorem ipsum dolor sit amet [...]" ); javaMailSender.send(mailMessage); return mailMessage; } } |
Запуск приложения
Теперь мы готовы запустить приложение. Если вы используете CLI, введите: gradle bootRun
, откройте браузер и перейдите к localhost:8080/mail
. На самом деле вы должны увидеть ошибку, которая говорит о том, что соединение с почтовым сервером не удалось. Как и ожидалось.
Поддельный SMTP-сервер
FakeSMTP — это бесплатный Fake SMTP-сервер с графическим интерфейсом, написанный на Java, для тестирования электронной почты в приложениях. Мы будем использовать его для проверки работоспособности представления. Пожалуйста, скачайте приложение и просто запустите его, java -jar fakeSMTP-<version>.jar
: java -jar fakeSMTP-<version>.jar
. После запуска поддельного SMTP-сервера, запустите сервер.
Теперь вы можете снова вызвать конечную точку REST и увидеть результат в Fake SMTP!
Но под тестированием я не имел в виду ручное тестирование! Приложение все еще полезно, но мы хотим автоматически проверить почтовый код.
Модульное тестирование почтового кода
Чтобы иметь возможность автоматически проверять отправку почты, мы будем использовать Wiser — инфраструктуру / утилиту для модульного тестирования почты на основе SubEtha SMTP . Простой низкоуровневый API-интерфейс SubEthaSMTP подходит для написания практически любых приложений для получения почты.
Использование Wiser очень просто. Во-первых, нам нужно добавить тестовую зависимость в build.gradle
: testCompile("org.subethamail:subethasmtp:3.1.7")
. Во-вторых, мы создаем интеграционный тест с JUnit, Spring и Wiser:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
@RunWith (SpringJUnit4ClassRunner. class ) @SpringApplicationConfiguration (classes = Application. class ) @WebAppConfiguration public class MailSubmissionControllerTest { private Wiser wiser; @Autowired private WebApplicationContext wac; private MockMvc mockMvc; @Before public void setUp() throws Exception { wiser = new Wiser(); wiser.start(); mockMvc = MockMvcBuilders.webAppContextSetup(wac).build(); } @After public void tearDown() throws Exception { wiser.stop(); } @Test public void send() throws Exception { // act mockMvc.perform(get( "/mail" )) .andExpect(status().isCreated()); // assert assertReceivedMessage(wiser) .from( "someone@localhosts" ) .to( "someone@localhost" ) .withSubject( "Lorem ipsum" ) .withContent( "Lorem ipsum dolor sit amet [...]" ); } } |
SMTP-сервер инициализируется, запускается в методе @Before
и останавливается в методе @Teardown
. После отправки сообщения утверждение сделано. Утверждение должно быть создано, поскольку структура не предоставляет никакого. Как вы заметите, нам нужно работать с объектом Wiser, который предоставляет список полученных сообщений:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
|
public class WiserAssertions { private final List<WiserMessage> messages; public static WiserAssertions assertReceivedMessage(Wiser wiser) { return new WiserAssertions(wiser.getMessages()); } private WiserAssertions(List<WiserMessage> messages) { this .messages = messages; } public WiserAssertions from(String from) { findFirstOrElseThrow(m -> m.getEnvelopeSender().equals(from), assertionError( "No message from [{0}] found!" , from)); return this ; } public WiserAssertions to(String to) { findFirstOrElseThrow(m -> m.getEnvelopeReceiver().equals(to), assertionError( "No message to [{0}] found!" , to)); return this ; } public WiserAssertions withSubject(String subject) { Predicate<WiserMessage> predicate = m -> subject.equals(unchecked(getMimeMessage(m)::getSubject)); findFirstOrElseThrow(predicate, assertionError( "No message with subject [{0}] found!" , subject)); return this ; } public WiserAssertions withContent(String content) { findFirstOrElseThrow(m -> { ThrowingSupplier<String> contentAsString = () -> ((String) getMimeMessage(m).getContent()).trim(); return content.equals(unchecked(contentAsString)); }, assertionError( "No message with content [{0}] found!" , content)); return this ; } private void findFirstOrElseThrow(Predicate<WiserMessage> predicate, Supplier<AssertionError> exceptionSupplier) { messages.stream().filter(predicate) .findFirst().orElseThrow(exceptionSupplier); } private MimeMessage getMimeMessage(WiserMessage wiserMessage) { return unchecked(wiserMessage::getMimeMessage); } private static Supplier<AssertionError> assertionError(String errorMessage, String... args) { return () -> new AssertionError(MessageFormat.format(errorMessage, args)); } public static <T> T unchecked(ThrowingSupplier<T> supplier) { try { return supplier.get(); } catch (Throwable e) { throw new RuntimeException(e); } } interface ThrowingSupplier<T> { T get() throws Throwable; } } |
Резюме
Всего за пару строк кода мы смогли автоматически протестировать почтовый код. Пример, представленный в этой статье, не сложен, но показывает, как легко начать работу с SubEtha SMTP и Wiser.
Как вы проверяете свой почтовый код?
Ссылка: | Тестирование почтового кода в приложении Spring Boot от нашего партнера по JCG Рафаля Боровца в блоге Codeleak.pl . |