Sunday, September 30, 2012

Testing Email with a Mock MailSender

If you have an application which sends out email, you don't want your unit tests doing that too, so you need to use a "mock mail sender". You can create one by extending JavaMailSenderImpl and overriding the send method so that it doesn't really send an email. Here is an example:
import java.util.Properties;

import javax.mail.internet.MimeMessage;

import org.springframework.mail.MailException;
import org.springframework.mail.MailPreparationException;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.mail.javamail.MimeMessagePreparator;

public class MockMailSender extends JavaMailSenderImpl {

  @Override
  public void send(final MimeMessagePreparator mimeMessagePreparator) throws MailException {
    final MimeMessage mimeMessage = createMimeMessage();
    try {
      mimeMessagePreparator.prepare(mimeMessage);
      final String content = (String) mimeMessage.getContent();
      final Properties javaMailProperties = getJavaMailProperties();
      javaMailProperties.setProperty("mailContent", content);
    } catch (final Exception e) {
      throw new MailPreparationException(e);
    }
  }
}
The mock shown above stores the email body into the mail properties object. This is a quick-and-dirty way of getting access to the content of the email just in case you want to check it in your unit test.

Here is the associated Spring Java-based configuration:

@Configuration
public class MailConfig {

  @Bean
  public JavaMailSender mailSender() {
    final JavaMailSenderImpl sender = new JavaMailSenderImpl();
    sender.setHost("mail.host.com");
    return sender;
  }

  @Bean
  public Notifier notifier() {
    return new Notifier(mailSender());
  }
}
The unit-test configuration:
@Configuration
@Profile("unit-test")
public class UnitTestMailConfig extends MailConfig {
  @Override
  @Bean
  public JavaMailSender mailSender() {
   return new MockMailSender();
  }
}
For more information about sending emails with Spring 3, see the documentation here.

Related Posts:
Spring 3 - JavaConfig: Unit Testing

Saturday, September 29, 2012

Installing Scala IDE for Eclipse Juno

Just spent ages trying to get the Scala IDE plugin working in Eclipse Juno (4.2).

Here are the links that finally worked for me:

To install them go to Eclipse > Help > Install new software... and enter the URL in the Work with... textfield.

stackoverflow - 40k rep

Eight months after crossing the 30k milestone, I've now achieved a reputation of 40k on stackoverflow! The following table shows some interesting stats about my journey so far:
0-10k 10-20k 20-30k 30-40k Total
Date achieved 01/2011 05/2011 01/2012 09/2012
Questions answered 546 376 253 139 1314
Questions asked 46 1 6 0 53
Tags covered 609 202 83 10 904
Badges
(gold, silver, bronze)
35
(2, 10, 23)
14
(0, 4, 10)
33
(2, 8, 23)
59
(3, 20, 36)
141
(7, 42, 92)
As I mentioned before, I have really enjoyed being a member of stackoverflow. For me, it has not simply been a quest for reputation, but more about learning new technologies and picking up advice from other experts on the site. I like to take on challenging questions, rather than the easy ones, because it pushes me to do research into areas I have never looked at before, and I learn so much during the process.

You can probably tell by the number of questions answered, that I haven't spent much time on stackoverflow recently. I've been busy at work and have also been participating in other stackexchange sites like superuser, serverfault and Unix and Linux.

50k, here I come!

Sunday, September 23, 2012

Spring 3 - JavaConfig: Unit Testing using a Different Profile

In unit tests, you should not connect to an external database or webservice. Instead, you should use an in-memory database like hsqldb and mock any other external system dependencies. In order to do so, you need to inject test beans into the Spring container instead of using real ones. This example shows how you can use a different configuration for unit testing using Spring Java-based configuration.

Let's start with the following configuration:

/**
 * Configuration for an external oracle database.
 */
@Configuration
public class DatabaseConfig {

  @Bean
  public DataSource personDataSource() {
    DataSource ds = new org.apache.commons.dbcp.BasicDataSource();
    ds.setDriverClassName("oracle.jdbc.driver.OracleDriver");
    ds.setUrl("jdbc:oracle:thin:@firefly:1521:HRP2");
    ds.setUsername("scott");
    ds.setPassword("tiger");
    return ds;
  }
}

/**
 * Main application config.
 */
@Configuration
@Import(DatabaseConfig.class)
public class AppConfig {

  @Bean
  public PersonDao personDao() {
    return new PersonDao(personDataSource());
  }
}
In order to use a different database for your unit tests, you need to create a separate unit test database configuration as shown below. This configuration returns an HSQL data source and, more importantly, is decorated with a @Profile annotation which indicates that it will be only be used when the "unit-test" profile is active.
/**
 * Configuration for an embedded HSQL database used by unit tests.
 */
@Configuration
@Profile("unit-test")
public class UnitTestDatabaseConfig extends DatabaseConfig {

  @Override
  @Bean
  public DataSource personDataSource() {
    return new EmbeddedDatabaseBuilder()
               .setType(EmbeddedDatabaseType.HSQL)
               .addScript("person.sql")
               .build();
  }
}
Now, write your unit test as shown below. The @ActiveProfiles annotation tells Spring which profile to use when loading beans for the test classes. Since it is set to "unit-test", the HSQL DataSource will be used.
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { AppConfig.class, UnitTestDatabaseConfig.class })
@ActiveProfiles("unit-test")
public class PersonDaoTest {

  @Autowired
  private PersonDao personDao;

  @Test
  public void testGetPerson() {
    Person p = personDao.getPerson("Joe");
  }
}

Saturday, September 22, 2012

Spring 3 - JavaConfig: Loading a Properties File

This example shows how you can load a properties file using Spring Java-based configuration and then use those properties in ${...} placeholders in other beans in your configuration.

First, you need to create a PropertySourcesPlaceholderConfigurer bean as shown below:

import org.springframework.context.annotation.*;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;

/**
 * Loads properties from a file called ${APP_ENV}.properties
 * or default.properties if APP_ENV is not set.
 */
@Configuration
@PropertySource("classpath:${APP_ENV:default}.properties")
public class PropertyPlaceholderConfig {

  @Bean
  public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
    return new PropertySourcesPlaceholderConfigurer();
  }
}
Next, import this configuration into your main application config and use @Value to resolve the ${...} placeholders. For example, in the code below, the databaseUrl variable will be set from the db.url property in the properties file.
import javax.sql.DataSource;
import org.springframework.context.annotation.*;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;

@Configuration
@Import(PropertyPlaceholderConfig.class)
public class AppConfig {

  @Value("${db.url}")      private String databaseUrl;
  @Value("${db.user}")     private String databaseUser;
  @Value("${db.password}") private String databasePassword;

  @Bean
  public DataSource personDataSource(){
    final DataSource ds = new org.apache.commons.dbcp.BasicDataSource();
    ds.setDriverClassName("oracle.jdbc.driver.OracleDriver");
    ds.setUrl(databaseUrl);
    ds.setUsername(databaseUser);
    ds.setPassword(databasePassword);
    return ds;
  }

  @Bean
  public PersonDao personDao() {
    return new PersonDao(personDataSource());
  }
}
Alternative approach:
Alternatively, you can load the properties file into the Spring Environment and then lookup the properties you need when creating your beans:
import javax.sql.DataSource;
import org.springframework.context.annotation.*;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.env.Environment;

@Configuration
@PropertySource("classpath:${APP_ENV:default}.properties")
public class AppConfig {

  @Autowired
  private Environment env;

  @Bean
  public DataSource personDataSource() {
    final DataSource ds = new org.apache.commons.dbcp.BasicDataSource();
    ds.setDriverClassName("oracle.jdbc.driver.OracleDriver");
    ds.setUrl(env.getProperty("db.url"));
    ds.setUsername(env.getProperty("db.user"));
    ds.setPassword(env.getProperty("db.password"));
    return ds;
  }

  @Bean
  public PersonDao personDao() {
    return new PersonDao(personDataSource());
  }
}
A minor downside of this approach is that you need to autowire the Environment into all your configs which require properties from the properties file.

Related posts:
Spring 3: JavaConfig vs XML Config

Sunday, September 16, 2012

Spring 3: JavaConfig vs XML Config

Spring JavaConfig allows you to configure the Spring IoC container and define your beans purely in Java rather than XML. I have been using Java-based configuration in all my new projects now and prefer it over the traditional XML-based configuration.

Here is a small example illustrating what the XML and Java configurations look like:

XML Config

<beans>
  <bean id="personDataSource" destroy-method="close" class="org.apache.commons.dbcp.BasicDataSource">
    <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
    <property name="url" value="jdbc:oracle:thin:@firefly:1521:HRP2"/>
    <property name="username" value="scott"/>
    <property name="password" value="tiger"/>
  </bean>
  <bean id="personDao" class="com.example.PersonDao">
    <property name="dataSource" ref="personDataSource"/>
  </bean>
</beans>
Java Config
import javax.sql.DataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppConfig {
  
  @Bean
  public DataSource personDataSource(){
    DataSource ds = new org.apache.commons.dbcp.BasicDataSource();
    ds.setDriverClassName("oracle.jdbc.driver.OracleDriver");
    ds.setUrl("jdbc:oracle:thin:@firefly:1521:HRP2");
    ds.setUsername("scott");
    ds.setPassword("tiger");
    return ds;
  }
  
  @Bean
  public PersonDao personDao() {
    return new PersonDao(personDataSource());
  }
}
Why I like JavaConfig:

Here are a few reasons, in no particular order, as to why I like the Java-based configuration:

  1. Easy to learn: With XML, I have always found myself copying the app-context.xml from the last project I did, to start off with. I also have a hard time remembering what the XML schema is. However, JavaConfig is so intuitive that you can easily start writing your configuration from scratch - all you need to remember are a few annotations. Java-based configuration is more succint than XML. It is also more "readable". This makes it easier to see, at a glance, how your spring container is configured as compared to reading a lot of XML.

  2. Quicker to write: It is faster to write JavaConfig because your Java IDE will help you complete class names and methods.

  3. Type safety: In XML, it is easy to type the name of a class or property incorrectly, but with JavaConfig this is not possible because you will be using code completion in your Java IDE. Even if you are not, you will get a compiler error and can fix it straightaway.

  4. Faster navigation: It is quicker to jump from one bean to another, track bean references etc because, since they are just Java classes and methods, you can use your IDE shortcuts to find types, go into method declarations and view call hierarchies.

  5. No context switching: Another advantage of JavaConfig is that your brain (and IDE) does not have to keep switching between XML and Java. You can stay happily in the Java world.

  6. No more XML! I hate XML in general. I find Spring XML verbose and hard to follow. It does not "belong" in a Java project. Sorry, but I don't think I will ever be using it again.

Oh, and YMMV :)

Saturday, September 15, 2012

Testing Expected Exceptions with JUnit Rules

This post shows how to test for expected exceptions using JUnit. Let's start with the following class that we wish to test:
public class Person {
  private final String name;
  private final int age;
    
  /**
   * Creates a person with the specified name and age.
   *
   * @param name the name
   * @param age the age
   * @throws IllegalArgumentException if the age is not greater than zero
   */
  public Person(String name, int age) {
    this.name = name;
    this.age = age;
    if (age <= 0) {
      throw new IllegalArgumentException("Invalid age:" + age);
    }
  }
}
In the example above, the Person constructor throws an IllegalArgumentException if the age of the person is not greater than zero. There are different ways to test this behaviour:

Approach 1: Use the ExpectedException Rule
This is my favourite approach. The ExpectedException rule allows you to specify, within your test, what exception you are expecting and even what the exception message is. This is shown below:

import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;

import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;

public class PersonTest {

  @Rule
  public ExpectedException exception = ExpectedException.none();
  
  @Test
  public void testExpectedException() {
    exception.expect(IllegalArgumentException.class);
    exception.expectMessage(containsString("Invalid age"));
    new Person("Joe", -1);
  }
}
Approach 2: Specify the exception in the @Test annotation
As shown in the code snippet below, you can specify the expected exception in the @Test annotation. The test will pass only if an exception of the specified class is thrown by the test method. Unfortunately, you can't test the exception message with this approach.
@Test(expected = IllegalArgumentException.class)
public void testExpectedException2() {
  new Person("Joe", -1);
}
Approach 3: Use a try-catch block
This is the "traditional" approach which was used with old versions of JUnit, before the introduction of annotations and rules. Surround your code in a try-catch clause and test if the exception is thrown. Don't forget to make the test fail if the exception is not thrown!
@Test
public void testExpectedException3() {
  try {
    new Person("Joe", -1);
    fail("Should have thrown an IllegalArgumentException because age is invalid!");
  } catch (IllegalArgumentException e) {
    assertThat(e.getMessage(), containsString("Invalid age"));
  }
}