I have a spring-boot Webflux application and I am writing some tests using Spock and Groovy.
My Controllers are secured with OAuth opaque token which I need to mock a response from introspection.
My test properties are:
spring.security.oauth2.resourceserver.opaquetoken.client-id=fake_client
spring.security.oauth2.resourceserver.opaquetoken.client-secret=fake_secret
spring.security.oauth2.resourceserver.opaquetoken.introspection-uri=http://localhost:8089/api/v1/oauth/token/introspect
My test uses WebClient as below:
webClient.post()
.uri(URL.toString()))
.accept(MediaType.APPLICATION_JSON)
.headers(http -> http.setBearerAuth("bearer_token"))
.exchange()
.expectStatus()
.is2xxSuccessful()
I found the solution.
You have to configure WireMockServer and then stub the response. Working solution below:
#ContextConfiguration
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class SomeTest extends Specification {
private static final int PORT = 8089;
private WireMockServer wireMockServer;
def setup() {
wireMockServer = new WireMockServer(options().port(PORT))
wireMockServer.start()
WireMock.configureFor("localhost", wireMockServer.port())
def stubIntrospection() {
stubFor(post("/api/v1/oauth/token/introspect")
.willReturn(aResponse()
.withStatus(200)
.withHeader("Content-Type", MediaType.APPLICATION_JSON_VALUE)
.withBodyFile("path-to-my-file.json")))
}
def "my test case" () {
stubIntrospection()
//my test here
}
}
There is no difference between Spock and JUnit when mocking spring security-context. You can provide with mocked Authentication for both unit (#WebFluxTest) and integration (#SpringBootTest) with either:
org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.mockOpaqueToken mutator for WebTestClient
#WithMockBearerTokenAuthentication annotation.
Mocked BearerTokenAuthentication, Spring's auth implementation for token introspection (unless you explicitly changed it in your conf), is put in test security-context directly by annotation or mutator: authorization header is not introspected (it is completely ignored and can be omitted).
Sample (with JUnit, just adapt to your test framework) in this project:
#Test
void securedRouteWithAuthorizedPersonnelViaMutatorIsOk() throws Exception {
api
.mutateWith(mockOpaqueToken()
.attributes(attributes -> attributes.put(StandardClaimNames.PREFERRED_USERNAME, "Ch4mpy"))
.authorities(new SimpleGrantedAuthority("ROLE_AUTHORIZED_PERSONNEL")))
.get().uri("https://localhost/secured-route")
.exchange()
.expectStatus().isOk();
}
#Test
#WithMockBearerTokenAuthentication(
authorities = "ROLE_AUTHORIZED_PERSONNEL",
attributes = #OpenIdClaims(preferredUsername = "Ch4mpy"))
void securedRouteWithAuthorizedPersonnelViaAnnotationIsOk() throws Exception {
api
.get().uri("https://localhost/secured-route")
.exchange()
.expectStatus().isOk();
}
When testing other secured #Component type than #Controller (like #Service or #Repository), only test annotation is usable as there is no HTTP request:
#Import({ SecurityConfig.class, SecretRepo.class })
class SecretRepoTest {
// auto-wire tested component
#Autowired
SecretRepo secretRepo;
#Test
void whenNotAuthenticatedThenThrows() {
// call tested components methods directly (do not use MockMvc nor
// WebTestClient)
assertThrows(Exception.class, () -> secretRepo.findSecretByUsername("ch4mpy").block());
}
#Test
#WithMockBearerTokenAuthentication(attributes = #OpenIdClaims(preferredUsername = "Tonton Pirate"))
void whenAuthenticatedAsSomeoneElseThenThrows() {
assertThrows(Exception.class, () -> secretRepo.findSecretByUsername("ch4mpy").block());
}
#Test
#WithMockBearerTokenAuthentication(attributes = #OpenIdClaims(preferredUsername = "ch4mpy"))
void whenAuthenticatedWithSameUsernameThenReturns() {
assertEquals("Don't ever tell it", secretRepo.findSecretByUsername("ch4mpy").block());
}
}
Annotation is available from
<dependency>
<groupId>com.c4-soft.springaddons</groupId>
<artifactId>spring-addons-oauth2-test</artifactId>
<scope>test</scope>
</dependency>
Which is a transient dependency of
<dependency>
<groupId>com.c4-soft.springaddons</groupId>
<artifactId>spring-addons-webflux-introspecting-test</artifactId>
<scope>test</scope>
</dependency>
I used the later in sample to also have WebTestClientSupport, but first is enough for just test annotations.
Related
I am trying to write a Unit tests for all of my service classes, but I cannot find a solution on how to mock a #PreAuthorize above my controller methods. As an example:
I have this function in controller:
#GetMapping("/users")
#PreAuthorize("hasAuthority('ADMIN')")
public ResponseEntity<List<User>> getUsers() {
return service.getUsers();
}
And this in my service class:
public ResponseEntity<List<User>> getUsers() {
return new ResponseEntity<>(userRepository.findAll(), HttpStatus.OK);
}
WebSecurity class:
protected void configure(HttpSecurity http) throws Exception {
http = http.cors().and().csrf().disable();
http = http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and();
http = http
.exceptionHandling()
.authenticationEntryPoint(
(request, response, ex) -> response.sendError(
HttpServletResponse.SC_UNAUTHORIZED,
ex.getMessage()
)
)
.and();
http.authorizeRequests()
.antMatchers(HttpMethod.GET, "/").permitAll()
.anyRequest().authenticated();
http.oauth2ResourceServer().jwt().jwtAuthenticationConverter(jwtAuthenticationConverter());
}
public JwtAuthenticationConverter jwtAuthenticationConverter() {
JwtAuthenticationConverter converter = new JwtAuthenticationConverter();
converter.setJwtGrantedAuthoritiesConverter(jwt ->
Optional.ofNullable(jwt.getClaimAsStringList("permissions"))
.stream()
.flatMap(Collection::stream)
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toList())
);
return converter;
}
Now I am trying to write a unit test:
#SpringBootTest
#AutoConfigureMockMvc
public class UserControllerTests {
#Autowired
private MockMvc mvc;
#MockBean
private UserService userService;
#Test
#WithMockJwtAuth(claims = #OpenIdClaims(otherClaims
= #Claims(stringClaims = #StringClaim(name = "permissions", value = "{ADMIN}"))))
public void getAllUsers_shouldBeSuccess() throws Exception {
ArrayList<User> users = new ArrayList<>();
users.add(new User("0", true, new Role("USER")));
when(userService.getUsers()).thenReturn(new ResponseEntity<>(users, HttpStatus.OK));
mvc.perform(get("/users"))
.andExpect(status().isOk());
}
}
But I receive an error on mvc.perform call:
java.lang.NoClassDefFoundError: `org/springframework/security/web/context/SecurityContextHolderFilter`
UPDATE:
I've tried https://github.com/ch4mpy/spring-addons and added #WithMockBearerTokenAuthentication, but I still receive the same error. Also to note: if I removed all # and left only with #Test above the method, I receive 401 error.
#Test
#WithMockBearerTokenAuthentication(attributes = #OpenIdClaims(otherClaims
= #Claims(stringClaims = #StringClaim(name = "permissions", value = "{ADMIN}"))))
public void getAllUsers_shouldBeSuccess() throws Exception {
ArrayList<User> users = new ArrayList<>();
users.add(new User("0", true, new Role("USER")));
when(userService.getUsers()).thenReturn(new ResponseEntity<>(users, HttpStatus.OK));
mvc.perform(get("/users"))
.andExpect(status().isOk());
}
Sadly, spring-security team chose to include in test framework MockMvc request post-processors and WebTestClient request mutators only, which limits OAuth2 authentication mocking to controllers unit-tests.
Hopefully, I kept my work on test annotations in a set of libs I publish on maven-central: https://github.com/ch4mpy/spring-addons. You can test any #Component with it (sample adapted from here):
//---------------------------------------------------//
// Test secured #Component which isn't a #Controller //
//---------------------------------------------------//
// Import web-security configuration and tested component class
#Import({ SampleApi.WebSecurityConfig.class, MessageService.class })
#ExtendWith(SpringExtension.class)
class MessageServiceTests {
// Auto-wire tested component
#Autowired
private MessageService messageService;
// Mock tested component dependencies
#MockBean
GreetingRepo greetingRepo;
#BeforeEach
public void setUp() {
when(greetingRepo.findUserSecret("ch4mpy")).thenReturn("Don't tel it");
}
#Test()
void greetWitoutAuthentication() {
// Call tested component methods directly (don't use MockMvc nor WebTestClient)
assertThrows(Exception.class, () -> messageService.getSecret("ch4mpy"));
}
#Test
#WithMockJwtAuth(authorities = "ROLE_AUTHORIZED_PERSONNEL", claims = #OpenIdClaims(preferredUsername = "ch4mpy"))
void greetWithMockJwtAuth() {
assertThat(messageService.getSecret("ch4mpy")).isEqualTo("Don't tel it");
}
}
Edit for updated question
Only #Controller unit-tests should run within the context of an HTTP request.
This means that #WebMvcTest (or #WebFluxTest) and MockMvc (or WebTestClient) must be used in #Controller tests only.
Unit-tests for any other type of #Component (#Service or #Repository for instance) should be written without the context of a request. This means that none of #WebMvcTest, #WebFluxTest, MockMvcand WebTestClient should be used in such tests.
The sample above shows how to structure such tests:
Trigger spring-context configuration
#Import web-security configuration and your #Component class
provide #MockBean for all of your #Component dependencies
in the test, call tested component methods directly (do not try to create a mockMvc request)
Edit for the 2nd question modification
I am trying to write a Unit tests for all of my service classes
Apparently, this first statement is not your main concern anymore as you're trying to write integration-tests for #Controller along with the #Services, #Repositories and other #Components it is injected. This is actually a completely different question than unit-testing each of those separately (mocking others).
NoClassDefFoundError on SecurityContextHolderFilter means that spring-security-web is not on your classpath. It should be, even during the tests. Check your dependencies (pom or gradle file that you did not include in your question)
Please also note that:
you might want to write value = "ADMIN" instead of value = "{ADMIN}" (unless you really want curly-braces in your authority name)
you can use just #WithMockJwtAuth("ADMIN") instead of #WithMockJwtAuth(claims = #OpenIdClaims(otherClaims = #Claims(stringClaims = #StringClaim(name = "permissions", value = "ADMIN"))))
you are allowed to read docs (you and I would save quite some time). This includes Spring doc and mine: home one and more importantly tutorials
Hi I have a Spring Boot (2.1.6 RELEASE) application, and I am trying add some simple integration test to my app.
Firstly I've create a base IntegrationTest class like below:
#TypeChecked
#Transactional
#Rollback
#SpringBootTest(classes = AppRunner.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
abstract class IntegrationTest extends Specification {
#Autowired
protected WebApplicationContext webApplicationContext
#Autowired
ObjectMapper objectMapper
MockMvc mockMvc
#Before
void setupMockMvc() {
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
.apply(springSecurity())
.build()
}
protected ResultActions makePost(final String uri, final Object dto) {
mockMvc.perform(post(uri)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(dtoToJson(dto))
)
}
private String dtoToJson(final Object dto) {
return objectMapper.writeValueAsString(dto)
}
}
This is my test method:
class TestForTest extends IntegrationTest {
#WithMockUser(username = "xxx#gmail.com")
def "should reset password and send mail with proper activation link"() {
given:
def email = "user2#xxx.com"
when:
ResultActions result = makePost("/rest/user/resendActivationMail", email)
then:
1 == 1
println(result)
}
}
Is strange because I have feeling the context was not be loaded, and so more there was no any try to do this.
I got error:
java.lang.IllegalArgumentException: WebApplicationContext is required
at org.springframework.util.Assert.notNull(Assert.java:198)
at org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder.<init>(DefaultMockMvcBuilder.java:52)
at org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup(MockMvcBuilders.java:51)
at pl.isolution.veolia.mveolia.spec.IntegrationTest.setupMockMvc(IntegrationTest.groovy:37)
Is there is a problem with Maven or IntellijIDEA? This took abot 340 ms, and I didn't see any attempt of context loading.
My stack:
JAVA 11, Maven 3, Spring Boot 2.1.6, Spock 1.3-groovy-2.5
Any suggestions?
Ok What a shame :) I missed a dependency.
<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-core</artifactId>
<version>1.3-groovy-2.5</version>
<scope>test</scope>
</dependency>
I tried junit with mockito, and wrote some test cases for a coding exercise.
Here is the test case which i wrote:
#RunWith(SpringRunner.class)
public class TransactionControllerTest {
#Mock
TransactionService transactionServiceMock;
#InjectMocks
TransactionController transactionController;
TransactionRequest txn = new TransactionRequest("123.34", "2018-11-28T23:32:36.312Z");
#Test
public void testSaveTxn() throws Exception {
Mockito.when(transactionServiceMock.saveTxn(Mockito.any(TransactionRequest.class))).thenReturn(true);
ResponseEntity<?> responseEntity = transactionController.saveTxn(null, txn);
assertTrue(responseEntity.getStatusCode().equals(HttpStatus.CREATED));
}
#Test
public void testGetStats() throws Exception {
StatsResponse sr = new StatsResponse("0.00", "0.00", "0.00", "0.00", 0L);
Mockito.when(transactionServiceMock.getStats()).thenReturn(sr);
ResponseEntity<StatsResponse> responseEntity = (ResponseEntity<StatsResponse>) transactionController.getStats(null);
System.out.println("sr response = "+responseEntity.getBody());
assertTrue(responseEntity.getBody().equals(sr));
}
#Test
public void testDelete() throws Exception {
Mockito.doNothing().when(transactionServiceMock).delete();
ResponseEntity<HttpStatus> responseEntity = (ResponseEntity<HttpStatus>) transactionController.deleteTxn(null);
System.out.println("sr response = "+responseEntity.getBody());
assertTrue(responseEntity.getStatusCode().equals(HttpStatus.NO_CONTENT));
}
}
The test cases were working fine.
But my application was rejected specifying the following reason:
You were using SpringRunner even though you are not using SpringContext in the tests, and mocking everything.
Now, following are my concerns:
What's wrong with the test cases?
What is the meaning of above rejection reason?
How can i correct that?
What's wrong with the test cases?
I think what they want you to do is to write a spring web layer test. This is not a spring MVC test/spring-boot test. Because you don't test the controller as a spring loaded resource. You test it as a simple java class. That won't prove whether it behaves as a Spring controller correctly. You won't be able to test features such as;
spring resource injection
request dispatching and validation
How can i correct that?
You have to write a spring MVC test and use MockMvc or RestTemplate to verify your controller. For example;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = YourContext.class)
#WebAppConfiguration
public class MyWebTests {
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
#Before
public void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
}
#Test
public void foo() throws Exception {
mockMvc.perform(get("/status"));
//and verification
}
}
Usage of mockito mocks is not the worst idea, but you could have used auto wired #MockBeans.
If this is spring-boot, you will have more flexibility. Have a look at following resources.
https://docs.spring.io/spring/docs/current/spring-framework-reference/testing.html
https://spring.io/guides/gs/testing-web/
You have complaint because you don't need spring's test features in your test.
Your test is pure unit test.
So if you will remove #RunWith(SpringRunner.class) nothing will be changed for your test. Just put there #ExtendWith(MockitoExtension.class)
SpringRunner will initialize spring context for you test that you could inject or mock slice of your application using following annotations:
#MockBean
#Autowired
etc..
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.
I have set up spring boot application using Gradle. Now I do understand that #EnableAutoConnfiguration configures the application based on dependencies in a class path. I am pretty happy to avoid all of the plumbing but things start happening which I wish wouldn't.
Here are my dependencies:
dependencies {
compile('org.springframework.boot:spring-boot-starter-web:1.2.3.RELEASE')
compile 'org.springframework.hateoas:spring-hateoas:0.17.0.RELEASE'
compile 'org.springframework.plugin:spring-plugin-core:1.2.0.RELEASE'
compile 'org.springframework.boot:spring-boot-starter-data-jpa'
compile 'com.google.guava:guava:18.0'
compile 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310'
compile 'commons-beanutils:commons-beanutils:1.9.2'
runtime 'org.hsqldb:hsqldb:2.3.2'
testCompile 'org.springframework.boot:spring-boot-starter-test'
testCompile 'com.jayway.jsonpath:json-path:2.0.0'
}
My application class:
#ComponentScan("org.home.project")
#SpringBootApplication
//#EnableHypermediaSupport(type = EnableHypermediaSupport.HypermediaType.HAL)
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
A snippet from UserController:
#RequestMapping(method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
public HttpEntity<ResourceSupport> create(#Valid #RequestBody UserCreateRequest ucr, BindingResult bindingResult) {
if (bindingResult.hasErrors()) throw new InvalidRequestException("Bad Request", bindingResult);
Long userId = userService.create(ucr);
ResourceSupport resource = new ResourceSupport();
resource.add(linkTo(UserEndpoint.class).withSelfRel());
resource.add(linkTo(methodOn(UserEndpoint.class).update(userId, null, null)).withRel(VIEW_USER));
resource.add(linkTo(methodOn(UserEndpoint.class).delete(userId)).withRel(DELETE_USER));
return new ResponseEntity(resource, HttpStatus.CREATED);
}
The UserController.java has two annotations:
#RestController
#RequestMapping(value = "/users", produces = MediaType.APPLICATION_JSON_VALUE)
First of - notice the commented out #EnableHyperdiaSupport annotation - links in the ResourceSupport instance are still serialized to hal+json format despite media type produced and media type set in the request. This happens automatically when 'org.springframework.plugin:spring-plugin-core:1.2.0.RELEASE' is introduced in the dependencies. How would one go about configuring it explicitly ?
Another issue are unit tests.
This passes:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = MockServletContext.class)
#WebAppConfiguration
public class UserControllerTest {
...ommited for brevity...
#InjectMocks
private UserController testObject;
#Before
public void setUp() throws Exception {
initMocks(this);
mockMvc = standaloneSetup(testObject).build();
}
#Test
public void testUserCreatedLinks() throws Exception {
mockMvc.perform(post("/users").contentType(MediaType.APPLICATION_JSON).content(data))
.andExpect(status().isCreated())
.andExpect(content().contentType(MediaType.APPLICATION_JSON)).andExpect(jsonPath("$.links.[*].rel", hasItem("self")));
}
...ommited fro brevity...
}
The post request returns a standard JSON response in the test - not HAL+JSON. Is there a way to reconfigure this so that unit testing #RestController with MockServletContext would produce HAL+JSON or getting back to problem number 1 - how to configure the response format explicitly so that Jackson serializer would not produce hal+json ?
You're running your test using Spring MVC Test's standaloneSetup which uses a bare minimum of configuration to get your controller up and running. That configuration isn't the same as the configuration that will be used when you run the whole application.
If you want to use the same configuration, you could use webAppContextSetup:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
#WebAppConfiguration
public class SomeTests {
#Autowired
private WebApplicationContext context;
private MockMvc mockMvc;
#Before
public void setUp() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context).build();
}
}
Alternatively, you can replicate Spring HATEOAS's configuration in the standalone setup. Note that this runs the risk of your tests' configuration deviating from your application's configuration. You'd create the MockMvc instance like this:
TypeConstrainedMappingJackson2HttpMessageConverter messageConverter =
new TypeConstrainedMappingJackson2HttpMessageConverter(ResourceSupport.class);
messageConverter.setSupportedMediaTypes(Arrays.asList(MediaTypes.HAL_JSON));
ObjectMapper objectMapper = messageConverter.getObjectMapper();
objectMapper.registerModule(new Jackson2HalModule());
objectMapper.setHandlerInstantiator(
new Jackson2HalModule.HalHandlerInstantiator(new DefaultRelProvider(), null));
MockMvc mockMvc = MockMvcBuilders.standaloneSetup(testObject)
.setMessageConverters(messageConverter).build();