krynio
krynio

Reputation: 2492

Test sending email in Spring

I want to test my services in spring which should send emails. I try to use org.subethamail:subethasmtp.

To acieve my goal I created service MySender where I send email:

@Autowired
private MailSender mailSender;

//...
    SimpleMailMessage message = new SimpleMailMessage();
    message.setTo("[email protected]");
    message.setSubject("Subject");
    message.setText("Text");
    mailSender.send(message);
// ...

To test this piece of code I created test application.properties (in test scope):

spring.mail.host=127.0.0.1
spring.mail.port=${random.int[4000,6000]}

And test configuration class which should start Wiser SMTP server and make it reusable in tests:

@Configuration
public class TestConfiguration {

    @Autowired
    private Wiser wiser;

    @Value("${spring.mail.host}")
    String smtpHost;

    @Value("${spring.mail.port}")
    int smtpPort;

    @Bean
    public Wiser provideWiser() {
        // provide wiser for verification in tests
        Wiser wiser = new Wiser();
        return wiser;
    }

    @PostConstruct
    public void initializeMailServer() {
        // start server
        wiser.setHostname(smtpHost);
        wiser.setPort(smtpPort);
        wiser.start();
    }

    @PreDestroy
    public void shutdownMailServer() {
        // stop server
        wiser.stop();
    }

}

Expected result is that application sends email using Wiser smtp server and verify number of sended messages.

But when I run service application throws MailSendException(Couldn't connect to host, port: 127.0.0.1, 4688; timeout -1;). But when I add breakpoint and try connect using telnet smtp server allow to connect and don't throw Connection refused.

Do you have any idea why I can't test sending mails?

Full code preview is available on github: https://github.com/karolrynio/demo-mail

Upvotes: 2

Views: 5847

Answers (2)

int21h
int21h

Reputation: 324

I faced same problem. If using some constant port number for spring.mail.port in test Spring configuration combined with Maven tests forking, it resulted in tests randomly failing on port conflict when starting Wiser. As noted here in comments, using random.int doesn't help - it returns different value each time it's referenced, and it's expected behavior (see this issue). Hence, we need a different way to initialize spring.mail.port with a random value, so it would be constant within the test execution. Here's a way to do it (thanks for advice here):

First, we may not set spring.mail.port in test properties file at all. We'll initialize it in TestPropertySource. We'll need a class like this:

public class RandomPortInitailizer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        int randomPort = SocketUtils.findAvailableTcpPort();
        TestPropertySourceUtils.addInlinedPropertiesToEnvironment(applicationContext,
                "spring.mail.port=" + randomPort);
    }
}

Now we can run our tests this way (not too different from what's found in OP):

@RunWith(SpringRunner.class)
@ContextConfiguration(initializers = RandomPortInitailizer.class)
public class WhenEmailingSomeStuff {
    @Value("${spring.mail.host}")
    String smtpHost;
    @Value("${spring.mail.port}")
    int smtpPort;

    @Before
    public void startEmailServer() {
        wiser = new Wiser();
        wiser.setPort(smtpPort);
        wiser.setHostname(smtpHost);
        wiser.start();
    }

    @After
    public void stopEmailServer() {
        wiser.stop();
    }

    @Test
    public void testYourJavaMailSenderHere() {
        //
    }
}

Upvotes: 1

Periklis Douvitsas
Periklis Douvitsas

Reputation: 2491

in the application properties can you also add

mail.smtp.auth=false
mail.smtp.starttls.enable=false

The change your code to have these extra two values

 @Value("${mail.smtp.auth}")
 private boolean auth;

 @Value("${mail.smtp.starttls.enable}")
 private boolean starttls;

and put these options in your initializeMailServer

Properties mailProperties = new Properties();
mailProperties.put("mail.smtp.auth", auth);
mailProperties.put("mail.smtp.starttls.enable", starttls);
wiser.setJavaMailProperties(mailProperties);
wiser.setHostname(smtpHost);
wiser.setPort(smtpPort);
wiser.start();

let me know if this worked for you

Upvotes: 0

Related Questions