5

I often test my application with a copy of a live database, but have to be careful to not do any actions that cause an email to be sent to a user. I'd love to have a way to configure spring so that when I'm in dev or test mode, that no email will get sent to real users. Ideally, I'd like for all of the emails that should have gone to the user to go instead to a mailbox that I can examine. And I don't want to change any code to make this happen, just xml config files.

I'm already using PropertyPlaceholderConfigurer and am reading in different property files based on if I'm running in production, test, or dev modes. I configure a JavaMailSenderImpl based on values in the property file. I'm also using a SimpleMailMessage to create a template with the From address.

Ideally there would be a way to rewrite to TO address of all outgoing emails to a test account if I'm running in dev or test mode.

My first thought was to use a different SMTP server for dev and test. But then I'd have to also manage another mail server, and would need to customize it so that it wouldn't send mail to anywhere, but would instead deliver it to a single mailbox. I don't want to add more management requirements if possible.

Maybe that's the best solution, but it seems like there should be a way to intercept the emails and change the recipient.

Has anyone dealt with this problem before? What solutions did you come up with?

Tauren
  • 26,795
  • 42
  • 131
  • 167
  • how are you executing your tests? – Bozho Jan 24 '10 at 14:43
  • bozho - at the moment, i don't have any unit tests that involve email. the system only sends email when a new account is created. but i now need to expand that to send notices on many different actions. and i don't want members to get these emails when testers are using the test server or devs are running locally. – Tauren Jan 24 '10 at 21:53
  • Check my answer http://stackoverflow.com/questions/8599791/a-simple-local-smtp-server/22043597#22043597 – m-szalik Mar 03 '14 at 15:14

6 Answers6

4

Note: my answer is predicated on using Maven, so this is more about building than anything, but using spring lets me conveniently inject the values.

I use a property-placeholder in and construct beans like:

jdbc.properties:

db.url=hostname:1521/test
db.username=awesome
db.password=password

applicationContext.xml (using context namespace)

<context:property-placeholder location="classpath:jdbc.properties"/>
<bean id="yo" class="some.DataSource">
    <property name="url" value="${db.url}"/>
    <property name="username" value="${db.username}"/>
    <property name="password" value="${db.password}"/>
</bean>

Then I store the environments in separate folders with domain specific values like:

src/main/environments
 ++ prod
 +++++ jdbc.properties
 ++ dev
 +++++ jdbc.properties
 ++ cert
 +++++ jdbc.properties

Using maven profiles (from pom.xml):

<profile>
    <id>environment</id>
    <activation>
        <property>
            <name>environment</name>
        </property>
    </activation>
    <build>
        <resources>
            <resource>
                <directory>src/main/environments/${environment}</directory>
            </resource>
        </resources>

You can run any maven command with the -Denviroment property to flex the properties:

mvn clean -Denvironment=dev test
mvn clean -Denvironment=cert package
mvn clean -Denvironment=prod deploy

etc. and any files from that environment folder are copied into the target or artifact along with any src/main/resources files.

hisdrewness
  • 7,599
  • 2
  • 21
  • 28
2

I've had a similar requirement in the past, and my solution was to write what I called EnvironmentSelectingFactoryBean (Spring does seem to encourage long class names...). This was a FactoryBean implementation that would "select" one of a range of available beans based on the current application envrironment.

If I show you some example XML, I think you'll get the idea:

<bean id="mailSender" class="com.kizoom.spring.config.EnvironmentSelectingFactoryBean">
   <property name="beanMap">
      <map>
         <entry key="test">
            <bean class="org.springframework.mail.javamail.JavaMailSenderImpl">
               <property name="host" value="${mailhost.test}"/>
            </bean>
         </entry>
         <entry key="production">
            <bean class="org.springframework.mail.javamail.JavaMailSenderImpl">
               <property name="host" value="${mailhost.production}"/>
            </bean>
         </entry>
      </map>
   </property>
</bean>

So, EnvironmentSelectingFactoryBean knows whether you're running in "test" or "production" mode, picks the appropriate bean from the beanMap, and spits that our from its FactoryBean.getObject() method. Both beans in the beanMap are real mail senders, not stubs, each with different proprty placeholder values, but you never run the risk of getting the wrong one.

Client code will just see a single JavaMailSender bean.

skaffman
  • 398,947
  • 96
  • 818
  • 769
1

I see that you state a specific requirement that you don't want to change any code. I still would like to suggest creating a stub implementation of JavaMailSender. That way, you could inject the stub object in your dev and test builds.

Buhb
  • 7,088
  • 3
  • 24
  • 38
  • Buhb -- actually, I don't mind creating a stub, like you mention. In fact, if it is shown to be the best solution, I could change other code at this point. I would just prefer to not change thing in my service layer. So I shouldn't have made that sound like such a strict requirement. – Tauren Jan 24 '10 at 21:56
1

Have a look at mock-javamail (does what the previous answer suggests) or a possibly even quicker solution is a fake SMTP server like Fakemail.

Mirko N.
  • 10,537
  • 6
  • 38
  • 37
  • akhnaten -- thanks, I wasn't aware of either of these tools, and they might certainly help. However, I'd prefer to have a way to still really send emails, but just have the TO address rewritten to an address that I specify. It doesn't look like these tools do that. – Tauren Jan 24 '10 at 22:04
  • With a fake SMTP server all emails are sent via SMTP as usual, except the SMTP server does not actually deliver them but saves them to local files where you can inspect them. So no need to change the recipient. – Mirko N. Jan 25 '10 at 11:04
1

The best solution is to create javax.mail.Session as JNDI resource. Than you can set it differently for prod / test and dev. And you do not have to worry about changing settings in your app because only difference is managed by the server.

For dev* and **test I can recommend you a good mock of javax.mail.Session.

A github project javaMail extension adds file transport that allows to:

  • save mails to files in text format instead of sending them
  • save mails to files in mbox format instead of sending them
  • add log info instead of sending email
m-szalik
  • 3,546
  • 1
  • 21
  • 28
1

Another option with Property Place holder is to dynamically load a property file depending on the System property at run time.

/WEB-INF/config/myapp.${myapp.env}.properties

WEB-INF/conf will have myapp.prod.properties myapp.stag.properties myapp.test.properties

Add -Dmyapp.env=prod to the tomcat or you application servers start up script.

Prabhakar
  • 46
  • 1