0

I'm thinking I need to setup the db testing environment (e.g. create tables, seed users so that token can be issued with credentials) before I can run tests but not sure how to.

@RunWith(SpringRunner.class)
@WebAppConfiguration
@SpringBootTest(classes = Application.class)
public class UsersControllerTest {

    // ...

    private MockMvc mockMvc;

    @Before
    public void setup() {
        this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac)
          .addFilter(springSecurityFilterChain).build();
    }

    private String obtainAccessToken(String username, String password) throws Exception {

        MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
        params.add("client_id", CLIENTID);
        params.add("grant_type", CLIENTPASSWORD);
        params.add("username", username);
        params.add("password", password);

        ResultActions result = mockMvc.perform(post("/oauth/token")
            .params(params)
            .with(httpBasic(CLIENTID, CLIENTPASSWORD))
            .accept("application/json;charset=UTF-8"))
            .andExpect(status().isOk())
            .andExpect(content().contentType("application/json;charset=UTF-8"));

        String resultString = result.andReturn().getResponse().getContentAsString();

        JacksonJsonParser jsonParser = new JacksonJsonParser();
        return jsonParser.parseMap(resultString).get("access_token").toString();
    }

    @Test
    public void givenNoToken_whenGetAllUsers_thenUnauthorized() throws Exception {

        mockMvc.perform(
            get("/users")
            ).andExpect(status().isUnauthorized());
    }

    @Test
    public void givenToken_whenGetAllUsers_thenOk() throws Exception {
        String accessToken = obtainAccessToken("martyn", "secret");
        mockMvc.perform(
            get("/users")
                .header("Authorization", "Bearer " + accessToken)
            ).andExpect(status().isOk());
    }

    // ...

Here is a typical Entity for this app:

@Entity(name = "users")
public class User implements Serializable {

    /**
     *
     */
    private static final long serialVersionUID = -8507204786382662588L;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @Column(nullable = false)
    private String firstName;

    @Column(nullable = false)
    private String surname;

    @Column(nullable = false, unique = true)
    private String email;

    @Column(nullable = false, unique = true)
    private String username;

    @Column(nullable = false)
    @JsonIgnore
    private String password;

    @OneToMany
    @JoinColumn(name="user_id") // cascade = CascadeType.ALL, orphanRemoval = true
    private List<Fund> funds;

    public Long getId() {
        return id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    // standard getters and setters

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getSurname() {
        return surname;
    }

    public void setSurname(String surname) {
        this.surname = surname;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public List<Fund> getFunds() {
        return funds;
    }
}

But also, as the error indicates, I'd need to generate these oauth* tables too.

Here is my src/test/resources/application.properties

spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.url=jdbc:h2:mem:db;DB_CLOSE_DELAY=-1
spring.datasource.username=sa
spring.datasource.password=sa

So I guess I want to generate the tables (entities, and oauth*) in the H2 database prior to running tests and populate with a single user(?) but can't seem to figure how this is done in Spring Boot. Or should I not be hitting any database and mocking JDBC altogether? Could someone point me in the correct direction as to how to prepare a test environment here? I'm at a bit of a loss.

UPDATE

Here is how dataSource is configured:

@Configuration
public class JDBCTokenConfig {

    @Value("${spring.datasource.url}")
    private String datasourceUrl;

    @Value("${spring.datasource.username}")
    private String dbUsername;

    @Value("${spring.datasource.password}")
    private String dbPassword;

    @Bean
    public DataSource dataSource() {
        final DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setUrl(datasourceUrl);
        dataSource.setUsername(dbUsername);
        dataSource.setPassword(dbPassword);
        return dataSource;
    }

    @Bean
    public TokenStore tokenStore(DataSource dataSource) {
        return new JdbcTokenStore(dataSource);
    }

    // @Bean
    // public TokenStore tokenStore() {
    //     return new InMemoryTokenStore();
    // }
}

pom.xml

<dependencies>
    ...
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    ...
</dependencies>
Martyn
  • 6,031
  • 12
  • 55
  • 121
  • Have a look at flyway (https://flywaydb.org) or Liquibase (https://www.liquibase.org). Both allow automated migrations, which will also trigger in development environments. – Alex B Dec 30 '19 at 19:24
  • Thanks for the response. These tables are not my own though, they belong to Spring Security for managing tokens. So I'm not comfortable writing migrations for these tables, but I get where you're coming from. Anyway I'm going to post a revision of this that differs slightly and is a little more specific. – Martyn Dec 30 '19 at 21:18

1 Answers1

0

I think it is a good thing to reach your in memory database without mocking. Honestly, you will need more time to configure rather than creating the correct schema needed for your database.

Using Spring-boot, it is very easy to configure to test your application:

  1. Declare using spring-boot-starter-jpa
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
  1. Add the in memory DB for your tests
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <scope>test</scope>
</dependency>
  1. Delete your spring.datasource properties in application.properties

Thanks to @SpringBootApplication, the datasource will be automatically configured to connect to your H2 in memory database.

  1. Create the SQL schema By default, spring-boot-starter-jpa configures automatically the datasource to execute scripts classpath:/schema.sql and if you need also, classpath:/data.sql. Create a schema.sql in src/test/resources, and create the tables (copy the following content, I think this is what you need: https://github.com/spring-projects/spring-security-oauth/blob/master/spring-security-oauth2/src/test/resources/schema.sql) (I am not sure for this, maybe hibernate creates your schema on his own).
    Create your schema in src/test/resources/schema.sql and seed the users in src/test/resources/data.sql

Check also the spring documentation to know how you can configure hibernate :

Now I understand, which is everybody, that you need to have your configuration different dependending on your environment. The best to do that is to rely on profiles.

In your situation, you have a prod profile, and a test profile.

Declare the profile and keep your spring.datasource properties in your src/test/resources/application.properties (the easier in your case I think)

I suggest you to read this Configure specific in memory database for testing purpose in Spring, and let me know if you have troubles configuring your environment.

With this, you will need to:

  • Add an annotation at the top of your Test class @ActiveProfiles('test')
  • Restore the spring.datasource properties you previously deleted an put them in src/test/resources/application-test.properties

Let me know

RUARO Thibault
  • 2,672
  • 1
  • 9
  • 14
  • Thanks for the response. I was missing `test` from the pom file for H2, so I've added that. I've removed everything from src/test/resources/application.properties file, however, I'm now seeing the following error - `Injection of autowired dependencies failed; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'spring.datasource.url' in value "${spring.datasource.url}"` - but I do still need this property for non-test environments. Btw I've updated my post to show where this is defined. – Martyn Dec 30 '19 at 21:58
  • Could you provide your POM as well ? When using `spring-boot-starter-data-jpa` or `spring-boot-starter-jdbc`, you don’t need to configure a Datasource yourself. It will be provided, and you can use this « provided » Datasource like any other regular bean – RUARO Thibault Dec 30 '19 at 22:21