Injecting Spring Beans into integration tests

This recipe is an example of how to inject Spring managed beans into integration test classes. Even for IT tests, whose first objective is to assess the backend as a blackbox, it is sometimes necessary to reach out technical objects from the intermediate layer.

Getting ready

We will see how to reuse an instance of a Spring managed datasource to be injected in our test class. This datasource will help us to build an instance of jdbcTemplate. From this jdbcTemplate, we will query the database and simulate/validate processes that couldn't be tested otherwise.

How to do it…

  1. We have @Autowired a dataSource SpringBean in our UserControllerIT test. This bean is defined in the test-specific Spring configuration file (spring-context-api-test.xml) resources directory (cloudstreetmarket-api):
    How to do it…
    <context:property-placeholderlocation="
      file:${user.home}/app/cloudstreetmarket.properties""/>
    <bean id="dataSource" 		
      class="org.apache.commons.dbcp2.BasicDataSource" 	
      destroy-method="close"">
      <property name="driverClassName"">
         <value>com.mysql.jdbc.Driver</value>
      </property>
      <property name="url"">
        <value>${db.connection.url}</value>
      </property>
    <property name="username"">
      <value>${db.user.name}</value>
    </property>
    <property name="password"">
      <value>${db.user.passsword}</value>
    </property>
    <property name="defaultReadOnly">
      <value>false</value>
    </property>
    </bean>

    A jdbcTemplate instance is created in the UserControllerIT class from the @Autowired dataSource bean:

        @Autowired
        private JdbcTemplate jdbcTemplate;
        @Autowired
        public void setDataSource(DataSource dataSource) {
        	this.jdbcTemplate = new JdbcTemplate(dataSource);
        }
  2. We use jdbcTemplate to insert and delete Social Connections directly in the database (see Chapter 5, Authenticating with Spring MVCA). This allows us to bypass and simulate a successful user OAuth2 authentication flow (that normally happens through the web browser).

    For deleting social connections, we have created the following private method that is called as needed by the test(s:):

        private void deleteConnection(String spi, String id) {
        	this.jdbcTemplate.update("delete from userconnection where providerUserId = ? and userId = "?", new Object[] {spi, id});
       }
  3. At the very top of the UserControllerIT class, the following two annotations can be noticed:
    • @RunWith(SpringJUnit4ClassRunner.class) tells JUnit to run with a custom extension of JUnit (SpringJUnit4ClassRunner) that supports the Spring TestContext Framework.
    • @ContextConfiguration("classpath:spring-context-api-test.xml")specifies where and how to load and configure the Spring application context:
          @RunWith(SpringJUnit4ClassRunner.class)
          @ContextConfiguration("classpath:spring-context-api-test.xml"")
          public class UserControllerIT extends AbstractCommonTestUser{
          private static User userA;
          private static User userB;
          ...
          }

How it works...

SpringJUnit4ClassRunner

In its design, the SpringJUnit4ClassRunner is a direct subclass of the JUnit's BlockJUnit4ClassRunner. SpringJUnit4ClassRunner that initializes when a TestContextManager is loaded. A TestContextManager manages the life cycle of a TestContext and can also reflect test events to the registered TestExecutionListeners (from @BeforeClass, @AfterClass, @Before, and @After annotations).

By loading a Spring context, the SpringJUnit4ClassRunner Spring context, SpringJUnit4ClassRunner enables the possibility use Spring managed beans in test classes. The SpringJUnit4ClassRunner also supports a set of annotations (either from JUnit or from Spring test) that can be used in test classes. The use of these annotations can be trusted for subsequently providing suitable life cycle management to context-defined objects.

Those annotations are @Test (with its expected and timeout annotation parameters), @Timed, @Repeat, @Ignore, @ProfileValueSourceConfiguration, and @IfProfileValue.

The @ContextConfiguration annotation

This class-level annotation is specific to Spring Test. It defines how and where to load a Spring Context for the test class.

Our definition in the recipe targets a specific Spring XML configuration file @ContextConfiguration("classpath:spring-context-api-test.xml").

However, since Spring 3.1 the contexts can be defined programmatically, @ContextConfiguration can also target configuration classes as follows:

@ContextConfiguration(classes={AnnotationConfig.class, WebSocketConfig.class})

As shown in the following snippet, both declaration types can be combined in the same annotation:

@ContextConfiguration(classes={AnnotationConfig.class, WebSocketConfig.class}, locations={"classpath:spring-context-api-test.xml"})

There is more…

We will see more in this section about the Spring JdbcTemplate that has been used for test purposes.

JdbcTemplate

In Chapter1, Setup Routine for an Enterprise Spring Application, we have introduced the different modules that make the Spring Framework what it is today. One group of modules is Data Access and Integration. This group contains the JDBC, ORM, OXM, JMS, and transactions modules.

The JdbcTemplate is a key-class part of the Spring JDBC core package. It reliably allows performing of database operations with straightforward utility methods and also provides an abstraction for big chunks of boilerplate code. Once more, this tool saves us time and offers patterns to design quality products.

Abstraction of boilerplate logic

Let's consider as an example the method in our test class that deletes connections:

jdbcTemplate.update("delete from userconnection where 
  providerUserId = ? and userId = "?", new Object[] {spi, id});

Using jdbcTemplate, deleting a database element is a one-line instruction. It creates a PreparedStatement under the hood, chooses the right Type, depending upon the arguments we actually pass as values, and it manages the database connection for us, making sure to close this connection whatever happens.

The jdbcTemplate.update method has been designed to issue a single SQL update operation. It can be used for inserts, updates, and also deletes.

As often in Spring, jdbcTemplate also transforms the produced checked exceptions (if any) into unchecked exceptions. Here, the potential SQLExceptions would be wrapped in a RuntimeException.

Extraction of auto-generated IDs

The jdbcTemplate.update method also offers other argument Types:

jdbcTemplate.update(final PreparedStatementCreator psc, final
  KeyHolder generatedKeyHolder);

In the case of an insert, this method can be called when needed to read and potentially reuse the generated ID (which is unknown before the query execution).

In our example, if we would have wanted to reuse the generated connection IDs when inserting new connections, we would have done it as follows:

KeyHolder keyHolder = new GeneratedKeyHolder();
jdbcTemplate.update(
  new PreparedStatementCreator() {
    public PreparedStatement createPreparedStatement(Connection 
    connection) throws SQLException {
    PreparedStatement ps = connection.prepareStatement("insert into userconnection (accessToken, ... , secret, userId ) values (?, ?, ... , ?, ?)", new String[] {"id""});
    ps.setString(1, generateGuid());
    ps.setDate(2, new Date(System.currentTimeMillis()));
    ...
    return ps;
    }
  }, keyHolder);
  Long Id = keyHolder.getKey().longValue();

But we didn't specifically require such a use case.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset