When i run junit test on class MailServiceTest it works fine, but when I'm trying to do mvn build -B it fails.
#RunWith(SpringRunner.class)
#SpringBootTest(classes = InstaticketapiApplication.class)
public class MailServiceTest {
#Autowired
private ApplicationProperties applicationProperties;
#Autowired
private MessageSource messageSource;
#Autowired
private SpringTemplateEngine springTemplateEngine;
#Spy
private JavaMailSenderImpl javaMailSender;
#Captor
private ArgumentCaptor messageCaptor;
#Autowired
private MailService mailService;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
doNothing().when(javaMailSender).send(any(MimeMessage.class));
mailService = new MailService();
mailService.setApplicationProperties(applicationProperties);
mailService.setJavaMailSender(javaMailSender);
mailService.setMessageSource(messageSource);
mailService.setTemplateEngine(springTemplateEngine);
}
#Test
public void testSendEmail() throws Exception {
mailService.sendEmail("nemanja#test.com", "testSubject", "testCont", false, false);
verify(javaMailSender).send((MimeMessage) messageCaptor.capture());
MimeMessage message = (MimeMessage) messageCaptor.getValue();
assertThat(message.getSubject()).isEqualTo("testSubject");
assertThat(message.getContent().toString()).isEqualTo("testCont");
assertThat(message.getAllRecipients()[0].toString()).isEqualTo("nemanja#test.com");
assertThat(message.getFrom()[0].toString()).isEqualTo("InstaTicket");
assertThat(message.getContent().getClass()).isEqualTo(String.class);
assertThat(message.getDataHandler().getContentType()).isEqualTo("text/plain; charset=UTF-8");
}
Full code could be find here https://github.com/sansajn5/ISA-InstaTicketAPI , I'm using yaml for properites.Also I used structure as Jhipster generated project
Normally you wouldn't load the Spring context file and then instantiate a component in a test via mailService = new MailService();
If you are writing a unit tests, don't load the Spring context file and mock all the dependencies of the component you intent to test.
If you are writing integration tests, don't mock dependencies, let the framework to load the context and wire dependencies.
The problem is this line: mailService = new MailService(). Remove it.
In simple language let the #Autowired annotation take care of instantiation in jUnit using SpringBoot.
Related
I am beginner with spring framework. I have a problem with configuring unit tests in spring boot, more precisely with loading spring context while running unit tests. I work with maven multimodule project (in team) and looking for the right solution to do this.
Part of my project structure is as follows:
commons (module, packaging:jar, utils module)
+--- src
+--- pom.xml
proposal (module, packaging:pom)
proposal-api (submodule: interfaces, dto, packaging:jar)
proposal-mapping (submodule: entities)
proposal-service (submodule: services, spring data repositories, dto - entity<->dto mappers, depends on proposal-api and proposal-mapping packaging:jar)
+--- src
+---main
+--- java
+---com.company.proposal.service
+--- DeviceRepositoryService.java
+---
DeviceMapper.java
+---
ProposalRepositoryService.java
+---
ProposalMapper.java
+---
and much more classes...
+--- test
+--- java
+---com.company.proposal.service
+---
DeviceRepositoryServiceTest.java
+---
ProposalRepositoryServiceTest.java
+---
...
+--- pom.xml
proposal-starter (submodule: autoconfiguration classes, packaging:jar)
+--- src
+---main
+--- java
+---com.company.proposal.configuration
+--- ProposalAutoConfiguration.java
+--- RemoteReportProcessorAutoConfiguration.java
+--- other configuration classes...
+---resources
+---META-INF
+--- spring.factories
+---application.properties
+--- pom.xml
entry-point (module, packaging: pom)
entry-point-api (submodule, packaging: jar)
entry-point-service (submodule, packaging: jar)
entry-point-starter (submodule, packaging: war deployed on wildfly)
other-modules ...
pom.xml (root pom)
Example unit test written by me (DeviceRepositoryServiceTest.java):
#RunWith(SpringRunner.class)
public class DeviceRepositoryServiceTest {
#Rule
public ExpectedException thrown = ExpectedException.none();
#MockBean
private DeviceRepository deviceRepository;
#Autowired
private DeviceMapper deviceMapper;
private DeviceRepositoryService deviceRepositoryService;
private final String imei = "123456789123456";
private final String producer = "samsung";
private final String model = "s5";
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
deviceRepositoryService = new DeviceRepositoryService(deviceRepository, deviceMapper);
}
#org.springframework.boot.test.context.TestConfiguration
static class TestConfiguration {
#Bean
public DeviceMapper deviceMapper() {
return new DeviceMapperImpl();
}
}
#Test
public void test_should_create_device() {
given(deviceRepository.findByImei(imei)).willReturn(null);
when(deviceRepository.save(any(Device.class))).thenAnswer((Answer) invocation -> invocation.getArguments()[0]);
DeviceSnapshot device = deviceRepositoryService.createOrFindDeviceByImei(imei, producer, model);
assertThat(device.getImei()).isEqualTo(imei);
assertThat(device.getProducer()).isEqualTo(producer);
assertThat(device.getModel()).isEqualTo(model);
verify(deviceRepository, times(1)).save(any(Device.class));
}
#Test
public void test_should_return_device() {
Device testDevice = createTestDevice();
given(deviceRepository.findByImei(imei)).willReturn(testDevice);
DeviceSnapshot actualDevice = deviceRepositoryService
.createOrFindDeviceByImei(testDevice.getImei(), testDevice.getProducer(), testDevice.getModel());
assertThat(actualDevice.getImei()).isEqualTo(testDevice.getImei());
assertThat(actualDevice.getProducer()).isEqualTo(testDevice.getProducer());
assertThat(actualDevice.getModel()).isEqualTo(testDevice.getModel());
verify(deviceRepository, times(0)).save(any(Device.class));
verify(deviceRepository, times(1)).findByImei(testDevice.getImei());
}
#Test
public void test_should_find_device() {
Device device = createTestDevice();
given(deviceRepository.findOne(device.getId())).willReturn(device);
DeviceSnapshot actualDevice = deviceRepositoryService.findDeviceById(device.getId());
DeviceSnapshot expectedDevice = deviceMapper.toDeviceSnapshot(device);
assertThat(actualDevice).isEqualTo(expectedDevice);
verify(deviceRepository, times(1)).findOne(device.getId());
}
#Test
public void test_should_find_device_by_pparams() {
Device device = createTestDevice();
Long proposalId = 1L, providerConfigId = 2L;
given(deviceRepository.findByProposalParams(proposalId, providerConfigId)).willReturn(device);
DeviceSnapshot actualDevice = deviceRepositoryService.findDeviceByProposalParams(proposalId, providerConfigId);
DeviceSnapshot expectedDevice = deviceMapper.toDeviceSnapshot(device);
assertThat(actualDevice).isEqualTo(expectedDevice);
verify(deviceRepository, times(1)).findByProposalParams(proposalId, providerConfigId);
}
#Test
public void test_should_throw_not_found_1() {
given(deviceRepository.findOne(anyLong())).willReturn(null);
this.thrown.expect(DeviceNotFoundException.class);
deviceRepositoryService.findDeviceById(1L);
}
#Test
public void test_should_throw_not_found_2() {
given(deviceRepository.findByProposalParams(anyLong(), anyLong())).willReturn(null);
this.thrown.expect(DeviceNotFoundException.class);
deviceRepositoryService.findDeviceByProposalParams(1L, 1L);
}
private Device createTestDevice() {
return Device.builder()
.id(1L)
.imei(imei)
.model(model)
.producer(producer)
.build();
}
}
As you can see I use #TestConfiguration annotation to define context, but because class DeviceRepositoryService is quite simple - only 2 dependencies so context definition is also simple. I also have to test class ProposalRepositoryService which looks as follows in short:
#Slf4j
#Service
#AllArgsConstructor
#Transactional
public class ProposalRepositoryService implements ProposalService {
private final ProposalRepository proposalRepository;
private final ProposalMapper proposalMapper;
private final ProposalRepositoryProperties repositoryProperties;
private final ImageProposalRepository imageProposalRepository;
private final ProviderConfigService providerConfigService;
...
}
In above class is more dependencies and the thing is I don't want to write bunch of configuration code for every test (TestConfiguration annotation). Eg. If I add some dependency to some service I would have to change half of my unit tests classes, also a lot of code repeats itself. I have also example when unit test code is getting ugly because of configuration definition:
#TestPropertySource("classpath:application-test.properties")
public class RemoteReportProcessorRepositoryServiceTest {
#Autowired
private RemoteReportProcessorRepositoryService remoteReportProcessorRepositoryService;
#TestConfiguration //here, I don't want to write bunch of configuration code for every test
static class TestConfig {
#Bean
#Autowired
public RemoteReportProcessorRepositoryService remoteReportProcessorRepositoryService(RemoteReportMailService remoteReportMailService,
FtpsService ftpsService,
RemoteDailyReportProperties remoteDailyReportProperties,
RemoteMonthlyReportProperties remoteMonthlyReportProperties,
DeviceRepository deviceRepository,
ProposalRepository proposalRepository) {
return new RemoteReportProcessorRepositoryService(ftpsService, remoteReportMailService, remoteDailyReportProperties, remoteMonthlyReportProperties, deviceRepository, proposalRepository);
}
#Bean
#Autowired
public FtpsManagerService ftpsManagerService(FTPSClient ftpsClient, MailService mailService, FtpsProperties ftpsProperties) {
return new FtpsManagerService(ftpsClient, ftpsProperties, mailService);
}
#Bean
public FTPSClient ftpsClient() {
return new FTPSClient();
}
#Bean
#Autowired
public MailService mailService(MailProperties mailProperties, JavaMailSender javaMailSender, PgpProperties pgpProperties) {
return new MailManagerService(mailProperties, javaMailSender, pgpProperties);
}
#Bean
public JavaMailSender javaMailSender() {
return new JavaMailSenderImpl();
}
#Bean
#Autowired
public RemoteReportMailService remoteReportMailService(RemoteReportMailProperties remoteReportMailProperties,
JavaMailSender javaMailSender,
Session session,
PgpProperties pgpProperties) {
return new RemoteReportMailManagerService(remoteReportMailProperties, javaMailSender, session, pgpProperties);
}
#Bean
#Autowired
public Session getJavaMailReceiver(RemoteReportMailProperties remoteReportMailProperties) {
Properties properties = new Properties();
properties.put("mail.imap.host", remoteReportMailProperties.getImapHost());
properties.put("mail.imap.port", remoteReportMailProperties.getImapPort());
properties.setProperty("mail.imap.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
properties.setProperty("mail.imap.socketFactory.fallback", "false");
properties.setProperty("mail.imap.socketFactory.port", remoteReportMailProperties.getImapPort().toString());
properties.put("mail.imap.debug", "true");
properties.put("mail.imap.ssl.trust", "*");
return Session.getDefaultInstance(properties);
}
}
...
}
So, my question is how to configure spring context for unit testing in spring boot maven multimodule project the right way, without writing bunch of configuration code?
I also will be grateful for the links to the articles when is describe in detail how to deal with maven multimodule projects.
After reading various articles and posts eg. Is it OK to use SpringRunner in unit tests? I realized that I don't need the entire application context when running tests, instead I should mock bean dependencies using plain #Mock annotation if testing without even involving and loading spring application context (which is faster). However, If I need some slice of application context (eg. to automatically load test properties or just for integration tests)
then I use spring boot annotations prepared for that: #WebMvcTest #JpaTest #SpringBootTest and so on.
Examples:
Plain Mock Test (without involving spring):
public class UserServiceImplTest {
#Mock
private UserRepository userRepository;
private UserServiceImpl userService;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
userService = new UserServiceImpl(userRepository);
}
/* Some tests here */
}
Test with slice of spring context:
#RunWith(SpringRunner.class)
#ActiveProfiles("test")
#EnableConfigurationProperties(value = DecisionProposalProperties.class)
#SpringBootTest(classes = {
DecisionProposalRepositoryService.class,
DecisionProposalMapperImpl.class
})
public class DecisionProposalRepositoryServiceTest {
#MockBean
private DecisionProposalRepository decisionProposalRepository;
#MockBean
private CommentRepository commentRepository;
#Autowired
private DecisionProposalRepositoryService decisionProposalRepositoryService;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
/* Some tests here */
}
Data jpa test:
#RunWith(SpringRunner.class)
#DataJpaTest
public class ImageProposalRepositoryTest {
#Autowired
private TestEntityManager entityManager;
#Autowired
private ImageProposalRepository imageProposalRepository;
#Test
public void testFindOne() throws Exception {
ImageProposal imageProposal = ImageProposal.builder()
.size(1024)
.filePath("/test/file/path").build();
entityManager.persist(imageProposal);
ImageProposal foundImageProposal = imageProposalRepository.findOne(imageProposal.getId());
assertThat(foundImageProposal).isEqualTo(imageProposal);
}
}
I have a REST (spring-hateoas) server that I'd like to test with a JUnit test. Therefore I am using an autoinjected TestRestTemplate.
But how do I now add some more configuration to this pre configured TestRestTemplate? I need to configure the rootURI and add interceptors.
Thisi s my JUnit Test class:
#RunWith(SpringRunner.class)
#ActiveProfiles("test")
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class RestEndpointTests {
private Logger log = LoggerFactory.getLogger(this.getClass());
#LocalServerPort
int localServerPort;
#Value(value = "${spring.data.rest.base-path}") // nice trick to get basePath from application.properties
String basePath;
#Autowired
TestRestTemplate client; // how to configure client?
[... here are my #Test methods that use client ...]
}
The documentation sais that a static #TestConfiguration class can be used. But inside that static class I cannot access localServerPort or basePath:
#TestConfiguration
static class Config {
#Bean
public RestTemplateBuilder restTemplateBuilder() {
String rootUri = "http://localhost:"+localServerPort+basePath; // <=== DOES NOT WORK
log.trace("Creating and configuring RestTemplate for "+rootUri);
return new RestTemplateBuilder()
.basicAuthorization(TestFixtures.USER1_EMAIL, TestFixtures.USER1_PWD)
.errorHandler(new LiquidoTestErrorHandler())
.requestFactory(new HttpComponentsClientHttpRequestFactory())
.additionalInterceptors(new LogRequestInterceptor())
.rootUri(rootUri);
}
}
My most important question: Why doesn't TestRestTemplate take spring.data.rest.base-path from application.properties into account in the first place? Isn't the idea of beeing complete preconfigured, the whole use case of this wrapper class?
The doc sais
If you are using the #SpringBootTest annotation, a TestRestTemplate is
automatically available and can be #Autowired into you test. If you
need customizations (for example to adding additional message
converters) use a RestTemplateBuilder #Bean.
How does that look like in a complete Java code example?
I know this is an old question, and you have probably found another solution for this by now. But I'm answering anyway for others stumbling on it like I did. I had a similar problem and ended up using #PostConstruct in my test class for constructing a TestRestTemplate configured to my liking instead of using #TestConfiguration.
#RunWith(SpringJUnit4ClassRunner.class)
#EnableAutoConfiguration
#SpringBootTest(classes = {BackendApplication.class}, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class MyCookieClientTest {
#LocalServerPort
int localPort;
#Autowired
RestTemplateBuilder restTemplateBuilder;
private TestRestTemplate template;
#PostConstruct
public void initialize() {
RestTemplate customTemplate = restTemplateBuilder
.rootUri("http://localhost:"+localPort)
....
.build();
this.template = new TestRestTemplate(customTemplate,
null, null, //I don't use basic auth, if you do you can set user, pass here
HttpClientOption.ENABLE_COOKIES); // I needed cookie support in this particular test, you may not have this need
}
}
In order to configure your TestRestTemplate, the official documentation suggests you to use the TestRestTemplate, as shown in the example below (for example, to add a Basic Authentication):
public class YourEndpointClassTest {
private static final Logger logger = LoggerFactory.getLogger(YourEndpointClassTest.class);
private static final String BASE_URL = "/your/base/url";
#TestConfiguration
static class TestRestTemplateAuthenticationConfiguration {
#Value("${spring.security.user.name}")
private String userName;
#Value("${spring.security.user.password}")
private String password;
#Bean
public RestTemplateBuilder restTemplateBuilder() {
return new RestTemplateBuilder().basicAuthentication(userName, password);
}
}
#Autowired
private TestRestTemplate restTemplate;
//here add your tests...
I had a situation, when I needed to use TestRestTemplate to access a REST endpoint on a remote server in our test environment. So the test did not start a Spring Boot application, rather than just connected to remote endpoint and consumed the REST service from there. The configuration of the test was simpler and execution is faster since it did not build up a complex Spring (Boot) context. Here is an extract from my configuration:
#RunWith(SpringRunner.class)
public class RemoteRestTestAbstract {
protected TestRestTemplate restTemplate;
private static RestTemplateBuilder restTemplateBuilder;
#BeforeClass
public static void setUpClass() {
restTemplateBuilder = new RestTemplateBuilder()
.rootUri("http://my-remote-test-server.my-domain.com:8080/");
}
#Before
public void init() {
restTemplate = new TestRestTemplate(restTemplateBuilder);
login();
}
//...
}
I'm using an autowired constructor in a service that when instantiated in the test class causes the #Value annotations to return null. Autowiring the dependencies directly solves the problem but the project follows the convention of using constructor based autowiring. My understanding is that instantiating the service in the test class is not creating it from the Spring IoC container which causes #Value to return null. Is there a way to create the service from the IoC container using constructor based autowiring without having to directly access the application context?
Example Service:
#Component
public class UpdateService {
#Value("${update.success.table}")
private String successTable;
#Value("${update.failed.table}")
private String failedTable;
private UserService userService
#Autowired
public UpdateService(UserService userService) {
this.userService = userService;
}
}
Example Test Service:
#RunWith(SpringJUnite4ClassRunner.class)
#SpringApplicationConfiguration(classes = {TestApplication.class})
#WebAppConfiguration
public class UpdateServiceTest {
private UpdateService updateService;
#Mock
private UserService mockUserService;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
updateService = new UpdateService(mockUserService);
}
}
To make #Value work updateService should be inside of spring context.
The best practice for spring framework integration tests is to include application context in test context and autowiring test source in test:
...
public class UpdateServiceTest {
#Autowired
private UpdateService updateService;
...
Mock userService
Option with changing userService to protected and considering that test and source classes are in same package.
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
updateService.userService = mockUserService;
}
Option with reflection with Whitebox:
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
Whitebox.setInternalState(updateService, 'userService', mockUserService);
}
The #Value is filled by a property placeholder configurer which is a post processor in the spring context. As your UpdateService is not part of the context it is not processed.
Your setup looks a little like a unclear mixture of unit and integration test. For a unit tests you will not need a spring context at all . Simply make the #Value annotated members package protected and set them or use ReflectionTestUtils.setField() (both shown):
public class UpdateServiceTest {
#InjectMocks
private UpdateService updateService;
#Mock
private UserService mockUserService;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
ReflectionTestUtils.setField(updateService, "successTable", "my_success");
updateService.failedTable = "my_failures";
}
}
For an integration test all wiring should be done by spring.
For this I added a inner config class providing the mock user service (the #Primary is only for the case you have any other user service in your context) and the mock is stored in a static member here to have simple access to the mock from the tests afterwards.
#RunWith(SpringJUnite4ClassRunner.class)
#SpringApplicationConfiguration(classes = {TestApplication.class, UpdateServiceTest.TestAddOn.class})
#WebAppConfiguration
public class UpdateServiceTest {
#Autowired
private UpdateService updateService;
private static UserService mockUserService;
static class TestAddOn {
#Bean
#Primary
UserService updateService() {
mockUserService = Mockito.mock(UserService.class);
return mockUserService;
}
}
}
Situation:
I am using Spring Cloud with Spring Boot in a microservice, that microservice is loading a DB config information to configure a connection.
I created a test to get the rest interfaces using Swagger for documentation.
I want to disable the loading of DB configuration because is not necessary.
Here is the code:
#WebAppConfiguration
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {Application.class, Swagger2MarkupTest.class}, loader = SpringApplicationContextLoader.class)
#ActiveProfiles("test")
public class Swagger2MarkupTest {
#Autowired
private WebApplicationContext context;
private MockMvc mockMvc;
#Autowired
protected Environment env;
#Before
public void setUp() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context).build();
}
#Test
public void convertSwaggerToAsciiDoc() throws Exception {
this.mockMvc.perform(get("/v2/api-docs").accept(MediaType.APPLICATION_JSON))
.andDo(Swagger2MarkupResultHandler.outputDirectory("target/docs/asciidoc/generated")
.withExamples("target/docs/asciidoc/generated/exampless").build())
.andExpect(status().isOk());
}
}
How can I run the test without loading the database configuration?
Is this possible?
There is an option to fake Spring bean with just plain Spring features. You need to use #Primary, #Profile and #ActiveProfiles annotations for it.
I wrote a blog post on the topic.
You can use in memory DB (e.g. H2) to replace real data source. Something like this:
#Configuration
public class TestingDataSourceConfig {
#Bean
#Primary
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.generateUniqueName(true)
.setType(H2)
.setScriptEncoding("UTF-8")
.ignoreFailedDrops(true)
.addScript("schema.sql")
.addScripts("user_data.sql", "country_data.sql")
.build();
}
}
I'm using Spring Boot 1.2.5-RELEASE. I have a controller that receive a MultipartFile and a String
#RestController
#RequestMapping("file-upload")
public class MyRESTController {
#Autowired
private AService aService;
#RequestMapping(method = RequestMethod.POST, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
#ResponseStatus(HttpStatus.CREATED)
public void fileUpload(
#RequestParam(value = "file", required = true) final MultipartFile file,
#RequestParam(value = "something", required = true) final String something) {
aService.doSomethingOnDBWith(file, value);
}
}
Now, the service works well. I tested it with PostMan and eveything goes as expected.
Unfortunately, I cannot write a standalone unit test for that code. The current unit test is:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = MyApplication.class)
#WebAppConfiguration
public class ControllerTest{
MockMvc mockMvc;
#Mock
AService aService;
#InjectMocks
MyRESTController controller;
#Before public void setUp(){
MockitoAnnotations.initMocks(this);
this.mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
}
#Test
public void testFileUpload() throws Exception{
final File file = getFileFromResource(fileName);
//File is correctly loaded
final MockMultipartFile multipartFile = new MockMultipartFile("aMultiPartFile.txt", new FileInputStream(file));
doNothing().when(aService).doSomethingOnDBWith(any(MultipartFile.class), any(String.class));
mockMvc.perform(
post("/file-upload")
.requestAttr("file", multipartFile.getBytes())
.requestAttr("something", ":(")
.contentType(MediaType.MULTIPART_FORM_DATA_VALUE))
.andExpect(status().isCreated());
}
}
Test fails with
java.lang.IllegalArgumentException: Expected MultipartHttpServletRequest: is a MultipartResolver configured?
Now, in the MultipartAutoConfiguration class from Spring Boot I see that a MultipartResolver is auto configured. But, I guess that with the standaloneSetup of MockMvcBuilders I cannot access this.
I tried several configurations of the unit test that I don't report for brevity. Especially, I also tried rest-assured as shown here, but honestly this doesn't work because it seems that I cannot mock the AService instance.
Any solution?
You are trying to combine here unit test (standaloneSetup(controller).build();) with Spring integration test (#RunWith(SpringJUnit4ClassRunner.class)).
Do one or the other.
Integration test will need to use something like code below. The problem would be faking of beans. There are ways to fake such bean with #Primary annotation and #Profile annotation (you create testing bean which will override main production bean). I have some examples of such faking of Spring beans (e.g. this bean is replaced by this bean in this test).
#Autowired
private WebApplicationContext webApplicationContext;
#BeforeMethod
public void init() {
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
Secodn option is to remove #RunWith(SpringJUnit4ClassRunner.class) and other class level configuration on your test and test controller without Spring Context with standalone setup. That way you can't test validation annotations on your controller, but you can use Spring MVC annotations. Advantage is possibility to fake beans via Mockito (e.g. via InjectMocks and Mock annotations)
I mixed what lkrnak suggested and Mockito #Spy functionality. I use REST-Assured to do the call. So, I did as follows:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = MyApplication.class)
#WebAppConfiguration
#IntegrationTest({"server.port:0"})
public class ControllerTest{
{
System.setProperty("spring.profiles.active", "unit-test");
}
#Autowired
#Spy
AService aService;
#Autowired
#InjectMocks
MyRESTController controller;
#Value("${local.server.port}")
int port;
#Before public void setUp(){
RestAssured.port = port;
MockitoAnnotations.initMocks(this);
}
#Test
public void testFileUpload() throws Exception{
final File file = getFileFromResource(fileName);
doNothing().when(aService)
.doSomethingOnDBWith(any(MultipartFile.class), any(String.class));
given()
.multiPart("file", file)
.multiPart("something", ":(")
.when().post("/file-upload")
.then().(HttpStatus.CREATED.value());
}
}
the service is defined as
#Profile("unit-test")
#Primary
#Service
public class MockAService implements AService {
//empty methods implementation
}
The error says the request is not a multi-part request. In other words at that point it's expected to have been parsed. However in a MockMvc test there is no actual request. It's just mock request and response. So you'll need to use perform.fileUpload(...) in order to set up a mock file upload request.