简体   繁体   中英

Spring boot: Inject beans from tests into web environment

I am building some integration tests for my sample application and was wondering if I can create some test data in my tests itself and then inject it through to the server that is running. I prefer not to mock my data because I want my test to run through the whole stack.

I understand the Spring Boot documentation is saying that the server and the tests are running in 2 separate threads, but is it possible to pass the same context through?

The code I have so far:

@ExtendWith(SpringExtension.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class ArtistResourceTests {
    @Autowired
    private TestRestTemplate restTemplate;

    @Autowired
    private ArtistRepository artistRepository;

    @Test
    @Transactional
    public void listReturnsArtists() {
        Artist artist = new DataFactory().getArtist();
        this.artistRepository.save(artist);

        ParameterizedTypeReference<List<Artist>> artistTypeDefinition = new ParameterizedTypeReference<List<Artist>>() {};
        ResponseEntity<List<Artist>> response = this.restTemplate.exchange("/artists", HttpMethod.GET, null, artistTypeDefinition);

        assertEquals(1, response.getBody().size());
    }
}

But this returns 0 results instead of 1 result.

I think you don't interact with some remotely running server.

SpringBootTest annotation starts the whole microservice locally inside the test. Otherwise if your test is just a series of calls to the remote server, you don't really need @SpringBootTest (and don't need spring boot at all :) ).

So you have an application context right inside the test. Now you're asking how to pre-populate the data. This is too broad, since you don't specify where exactly the data is stored and which data persistence layers are involved (RDBMS, Elasticsearch, Mongo, ...)?

One possible general-purpose way is using Test Execution Listener that can have a method beforeTestMethod .

The application context is started so you can really prepare the data in a custom way and save it to the database of your choice (via injected DAOs into the listener or something).

Another interesting way if you use Flyway is to provide migrations in the src/test/resources/data folder so that flyway will execute migrations automatically during the test.

Update

The comment states, that H2 DB is used, in this case, assuming the datasource is configured correctly and indeed provides connections to H2, the easiest way is running SQL scripts with data inserts:

@Sql(scripts = {"/scripts/populateData1.sql", ..., "/scripts/populate_data_N.sql"}, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
public void myTest() {
...
}

Now if you have to work with

this.artistRepository.save(artist);

Then spring doesn't care about threads. It can inject any data as long as the "data" is a bean (or resource), since you're working with objects (Artist), it has to be a bean.

So create TestConfiguration with bean Artist , make sure its in the same package as test (so that spring boot test scanning process will load the configuration) and inject it into the test with @Autowired as usual:

@TestConfiguration
public class ArtistsConfiguration {

   @Bean 
   public Artist artistForTests() {
       return new Artist(...);
   }
}


@Import(ArtistsConfiguration.class)
@SpringBootTest
public class MyTest {

   @Autowired
   private Artist artist;

   ....
}

You can use @ContextConfiguration, like so:

@RunWith(SpringJUnit4ClassRunner.class)
@ActiveProfiles("test")
@ContextConfiguration(locations = {"/testApplicationContext.xml"})
public class TestGetPerson {

    @Autowired
    private PersonService personService;
...

Then in testApplicationContext you specify the packages Spring should scan:

<context:component-scan base-package="nl.example.server.service"/>
<context:component-scan base-package="nl.example.server.test.db"/>

There may be an annotation that achieves the same thing. What I do is make Spring scan most packages the same as in the live application, except the database components: I use an inmemory H2 database for testing. The Profiles annotation tells Spring what classes are to be use in test. Also, you can configure (using @Configuration in a scanned package) certain classes to be Mockito mocks:

@Configuration
@Profile("test")
public class CustomerManagerConfig {
@Bean("customerManager")

    public CustomerManager customerManager() {
        return Mockito.mock(CustomerManager.class);
    }
}

This doesn't run your test data against a separate server, but it does run the test in an environment that ressembles your application's environment as closely as you like.

Regarding your question, instead of using a Mockito mock, you can create your own mock that injects your test data in any of the components you like.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM