Reputation: 17713
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?
Upvotes: 1
Views: 5425
Reputation: 17713
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
}
Upvotes: 1
Reputation: 5008
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.
Upvotes: 0
Reputation: 24561
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)
Upvotes: 1