I have a Spring Boot 2 web service. It accepts a Bearer token and uses Spring OAuth2 to go to an external URL and grab the users details using that token. I'm trying to create tests that test the implementation of the web service, but I'm struggling to figure out how to pass the AuthenticationPrinicple from a test?
Here is an example of a method in my controller;
#RequestMapping(value = "", method = RequestMethod.GET, produces = "application/json")
public ResponseEntity listUserUploadedFiles(#AuthenticationPrincipal Client client) {
FileListResponse rsp = new FileListResponse(fileStorageService.getUserFiles(client.getUid()));
return rsp.response(HttpStatus.OK);
}
Here is my test so far;
#Test
public void TestSimple() throws Exception {
mockMvc.perform(get("/file"))
.andExpect(status().isOk());
}
Currently the test fails because the Client (AuthenticationPrinicple) is null.
How would I do this?
---- UPDATE ----
Here is my Client object, as you can see, it doesn't inherit UserDetails.
public class Client implements Serializable {
private static final long serialVersionUID = 1L;
private String uid;
private String email;
public String getUid() {
return uid;
}
public void setUid(String uid) {
this.uid = uid;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
There is also a PrincipalExtractor bean setup.
#Bean
public PrincipalExtractor principalExtractor() {
return map -> {
Client client = new Client();
client.setUid(String.valueOf(map.get("uid")));
client.setEmail(String.valueOf(map.get("name")));
return client;
};
}
To provide a little more information on the project. This is a microservice, the user authenticates via a web application which takes place externally to this service. When calls to this server are made, a token is supplied which is verified with an authenticate server (again, external). The response from authentication server provides us with a uuid and an email field, the PrincipalExtractor maps these to a Client object, which is then passed into the controller to be used.
You could mock your user when testing #WithMockUser.
If you use MockMvc there is no extra sec-config needed
#RunWith(SpringRunner.class)
#WebMvcTest(SecuredController.class)
public class SecuredControllerWebMvcIntegrationTest {
#Autowired
private MockMvc mockMvc;
#Test
#WithMockUser(value = "user")
public void testSimple() throws Exception {
mockMvc.perform(get("/file"))
...
if you want to use #WithMockUser in a #SpringBootTest you need extra configuration
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class SecuredControllerSpringBootIntegrationTest
...
#Autowired
private WebApplicationContext context;
private MockMvc mvc;
#Before
public void setup() {
mvc = MockMvcBuilders
.webAppContextSetup(context)
.apply(springSecurity())
.build();
}
...
ref spring-security-integration-tests
Related
I have exception-messages written down in the application.yml. They are pure text, which is later reformatted using java.text.MessageFormat.
I have got the following custom RuntimeException my service throws when login failed:
#Component
public class AccountLoginFailedException extends RuntimeException {
#Autowired
public AccountLoginFailedException(#Value("#(${authservice.exception-messages.login-failed})") final String message, #Qualifier(value = "Credentials") final Credentials credentials) {
super(MessageFormat.format(message, credentials.getUsername()));
}
}
My test, which solely tests the AccountController and mocks away the service behind it:
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = AuthServiceTestConfiguration.class)
#WebMvcTest(AccountController.class)
public class AccountControllerTest {
#Autowired
private BeanFactory beanFactory;
#Autowired
private ObjectMapper objectMapper;
#Autowired
private TestHelper helper;
#MockBean
private AccountService accountService;
#Autowired
private JwtService jwtService;
#Autowired
private MockMvc mvc;
#Test
public void test_LoginFailed_AccountDoesNotExist() throws Exception {
// Given
final Credentials credentials = helper.testCredentials();
final String credentialsJson = objectMapper.writeValueAsString(credentials);
final AccountLoginFailedException loginFailedException = beanFactory.getBean(AccountLoginFailedException.class, credentials);
// When
given(accountService.login(credentials)).willThrow(loginFailedException);
// Then
mvc
.perform(
post("/login")
.accept(MediaType.APPLICATION_JSON_UTF8_VALUE)
.contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)
.content(credentialsJson))
.andExpect(status().isUnprocessableEntity())
.andExpect(jsonPath("$.data").value(equalTo(loginFailedException.getMessage())));
}
}
message contains the correct String. However: credentials contains just an empty object (not null) instead of the one created using helper.testCredentials().
Here is a slightly simplified TestHelper class I am using:
#TestComponent
public class TestHelper {
public static final String USERNAME = "SomeUsername";
public static final String PASSWORD = "SomePassword";
#Autowired
private BeanFactory beanFactory;
public Credentials testCredentials() {
final Credentials credentials = beanFactory.getBean(Credentials.class.getSimpleName(), Credentials.class);
credentials.setUsername(USERNAME);
credentials.setPassword(PASSWORD);
return credentials;
}
}
These custom exceptions are thrown by my application only and are always expected to contain the credentials (username) responsible for it. I also have a AccountExceptionsControllerAdvice-class, which just wraps these custom exceptions in a generic JSON response, exposing the error in a preferred manner.
How can I ensure that this particular instance of Credentials is inserted into the particular instance of AccountLoginFailedException? Or should I not be autowiring exceptions at all?
You could mock your Credentials component in your tests as follows:
#MockBean
private Credentials credentials;
#Before
public void before() {
when(credentials.getUsername()).thenReturn(USERNAME);
when(credentials.getPassword()).thenReturn(PASSWORD);
}
I would like to create Test for my rest controller:
#Controller
#RequestMapping("/v2/api/show/project")
public class ApiAccessController {
private final ApiAccessService apiAccessService;
#Autowired
ApiAccessController(ApiAccessService apiAccessService){
this.apiAccessService = apiAccessService;
}
#PutMapping(value = "/{id}/apikey")
public ResponseEntity<ApiKeyResponse> generateApiKey(#PathVariable("id")Long id, Principal principal) {
return apiAccessService.generateApiKey(id, principal.getName());
}
}
My test looks as follow:
#RunWith(SpringJUnit4ClassRunner.class)
public class ApiAccessControllerTest {
private MockMvc mockMvc;
Principal principal = new Principal() {
#Override
public String getName() {
return "TEST_PRINCIPAL";
}
};
#InjectMocks
ApiAccessController apiAccessController;
#Mock
ProjectRepository projectRepository;
#Before
public void setUp(){
mockMvc = MockMvcBuilders.standaloneSetup(apiAccessController).build();
}
#Test
public void testGenerateApiKey() throws Exception {
Project project = new Project();
project.setId((long) 1);
project.setName("test");
project.setDescription("testdesc");
project.setCiid("ciid");
when(projectRepository.save(any(Project.class))).thenReturn(project);
mockMvc.perform(MockMvcRequestBuilders.put("/v2/api/show/project/" + project.getId() +"/apikey").principal(principal))
.andExpect(MockMvcResultMatchers.status().isOk());
}
}
Which is ment to create project and then run generateApiKey on this project, however I get NullpointerException looking like mocked controller cannot find created entity
could anyone please point me in the right direction as I am just starting with testing?
You should mock ApiAccessService instead of ProjectRepository.
Have a look at the code:
#RunWith(SpringJUnit4ClassRunner.class)
public class ApiAccessControllerTest {
private MockMvc mockMvc;
private Principal principal = () -> "TEST_PRINCIPAL";
#InjectMocks
private ApiAccessController apiAccessController;
#Mock
private ApiAccessService apiAccessService;
#Before
public void setUp() {
mockMvc = MockMvcBuilders.standaloneSetup(apiAccessController).build();
}
#Test
public void testGenerateApiKey() throws Exception {
long id = 1L;
when(apiAccessService.generateApiKey(id, principal.getName())).thenReturn(new ApiKeyResponse(111L));
mockMvc.perform(MockMvcRequestBuilders.put("/v2/api/show/project/{id}/apikey", id).principal(principal))
.andExpect(MockMvcResultMatchers.status().isOk());
}
}
If you want to create integration test, that tests ApiAccessController -> ApiAccessService -> ProjectRepository integration you need to load your context (use for example #SpringBootTest).
Also you need to fix controller, use ResponseEntity.ok(...) :
#PutMapping(value = "/{id}/apikey")
public ResponseEntity<ApiKeyResponse> generateApiKey(#PathVariable("id") Long id, Principal principal) {
return ResponseEntity.ok(apiAccessService.generateApiKey(id, principal.getName()));
}
You can find really good examples of all test types in this repository MVC tests examples
The Mock you are creating is not referenced in the Controller. The Service you reference in the Controller is not part of your test setup. Therefore any access to the Service will cause a NullPointerException as the Service is not set.
I have a problem with testing the REST API using MOCKITO
I have an example of the rest controller code:
#RestController
#RequestMapping(path = "api/workers")
public class WorkOfferController {
#Autowired
private WorkerService workerService;
#PostMapping(value = "/lists", consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity searchWorker(#RequestBody RecruitmentSearchRequest recruitmentSearchRequest, #RequestParam("page") int page, #RequestParam("size") int size,) throws NoSuchFieldException {
System.err.print('WorkerController');
return workerService.getWorkers(recruitmentSearchRequest, page, size);
}
}
And the right service for it:
#Service
#Transactional
#RequiredArgsConstructor(onConstructor = #__(#Autowired))
public class WorkerService {
private final WorkerRepistory workerRepository;
private final UserRepository userRepository;
public ResponseEntity getWorkers(RecruitmentSearchRequest recruitmentSearchRequest,int pageNumber, int size) throws NoSuchFieldException {
System.err.print('WorkerService');
...
}
}
I want to test whether everything is okay under the url with the right data. I do not want to use this database because I prefer Mockito.
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#SpringBootTest(classes = Appp2workApplication.class)
#FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class PharmacyWorkOfferRestDocsTests {
private MockMvc mockMvc;
#InjectMocks
private WorkOfferController workOfferController;
#Mock
private WorkerService workerService;
#Mock
UserRepository userRepository;
#Mock
WorkerRepistory workerRepository;
#Before
public void setUp() {
this.mockMvc = MockMvcBuilders.standaloneSetup(workOfferController).build();
}
#Test
public void searchWorkOfferListWithParameters() throws Exception {
String payload = "{\n" +
"\t\"name\":\"tomek\",\n" +
"\t\"searchFromSalary\":5,\n" +
"}";
Mockito.doNothing().when(userRepository.save(Mockito.anyObject()));
Mockito.when(searchService.getRecruitmentOfferJobListWithParameters(Mockito.anyObject())).thenReturn(list);
this.mockMvc.perform(post("/api/workers/lists?page=0&size=20").contentType(MediaType.APPLICATION_JSON).content(payload))
.andExpect(status().isOk());
}
}
And I have a problem that with this approach the test comes to me in the controller and displays "WorkerController" but I do not want to enter the service from this controller, and it returns 200, but it really only came to the controller and that's it. This is probably because WorkerService is as Mock but I tried to give it as eg Autowired or InjectMock and it is still the same.
What do I do wrong that I enter into the controller but I do not want to use this controller for the appropriate service?
I'm new to Zuul J-unit testing. I have a couple of filters which is ChangeRequestEntityFilter and SessionFilter, Where I pasted my filtercode below. Can someone tell me how to write a Junit for the filter. I've searched and trying to use MockWire for the unit testing(Also I pasted my empty methods with basic annotations and WireMock port). I need at-least one proper example how this J-unit for Zuul works. I've referred the http://wiremock.org/docs/getting-started/ doc. Where I got what to do, but not how to do.
public class ChangeRequestEntityFilter extends ZuulFilter {
#Autowired
private UtilityHelperBean utilityHelperBean;
#Override
public boolean shouldFilter() {
// //avoid http GET request since it does'nt have any request body
return utilityHelperBean.isValidContentBody();
}
#Override
public int filterOrder() {
//given priority
}
#Override
public String filterType() {
// Pre
}
#Override
public Object run() {
RequestContext context = getCurrentContext();
try {
/** get values profile details from session */
Map<String, Object> profileMap = utilityHelperBean.getValuesFromSession(context,
CommonConstant.PROFILE.value());
if (profileMap != null) {
/** get new attributes need to add to the actual origin microservice request payload */
Map<String, Object> profileAttributeMap = utilityHelperBean.getProfileForRequest(context, profileMap);
/** add the new attributes in to the current request payload */
context.setRequest(new CustomHttpServletRequestWrapper(context.getRequest(), profileAttributeMap));
}
} catch (Exception ex) {
ReflectionUtils.rethrowRuntimeException(new IllegalStateException("ChangeRequestEntityFilter : ", ex));
}
return null;
}
}
I know ,I'm asking more. But give me any simple working complete example, I'm fine with it.
My current code with basic annotations and WireMock port.
#RunWith(SpringRunner.class)
#SpringBootTest
#DirtiesContext
#EnableZuulProxy
public class ChangeRequestEntityFilterTest {
#Rule
public WireMockRule wireMockRule = new WireMockRule(8080);
#Mock
ChangeRequestEntityFilter requestEntityFilter;
int port = wireMockRule.port();
#Test
public void changeRequestTest() {
}
}
Have you tried #MockBean?
https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/test/mock/mockito/MockBean.html
"When #MockBean is used on a field, as well as being registered in the application context, the mock will also be injected into the field. Typical usage might be:"
#RunWith(SpringRunner.class)
public class ExampleTests {
#MockBean
private ExampleService service;
#Autowired
private UserOfService userOfService;
#Test
public void testUserOfService() {
given(this.service.greet()).willReturn("Hello");
String actual = this.userOfService.makeUse();
assertEquals("Was: Hello", actual);
}
#Configuration
#Import(UserOfService.class) // A #Component injected with ExampleService
static class Config {
}
}
Here there is another approach:
private ZuulPostFilter zuulPostFilter;
#Mock
private anotherService anotherService;
#Mock
private HttpServletRequest request;
#Before
public void before() {
MockitoAnnotations.initMocks(this);
MonitoringHelper.initMocks();
zuulPostFilter = new ZuulPostFilter(anotherService);
doNothing().when(anotherService).saveInformation(null, false);
}
#Test
public void postFilterTest() {
log.info("postFilterTest");
RequestContext context = new RequestContext();
context.setResponseDataStream(new ByteArrayInputStream("Test Stream".getBytes()));
context.setResponseGZipped(false);
RequestContext.testSetCurrentContext(context);
when(request.getScheme()).thenReturn("HTTP");
RequestContext.getCurrentContext().setRequest(request);
ZuulFilterResult result = zuulPostFilter.runFilter();
assertEquals(ExecutionStatus.SUCCESS, result.getStatus());
assertEquals("post", zuulPostFilter.filterType());
assertEquals(10, zuulPostFilter.filterOrder());
}
In this case you can test the filter and mock the services inside it without having to autowire it, the problem with the #autowired is that if you have services inside the filter, then it is going to be an integration test that is going to be more difficult to implement.
I'm using SpringBoot 2 and Spring 5 (RC1) to expose reactive REST services. but I can't manage to write unit test for those controllers.
Here is my controller
#Api
#RestController
#RequestMapping("/")
public class MyController {
#Autowired
private MyService myService;
#RequestMapping(path = "/", method = RequestMethod.GET)
public Flux<MyModel> getPages(#RequestParam(value = "id", required = false) String id,
#RequestParam(value = "name", required = false) String name) throws Exception {
return myService.getMyModels(id, name);
}
}
myService is calling a database so I would like not to call the real one. (I don't wan't integration testing)
Edit :
I found a way that could match my need but I can't make it work :
#Before
public void setup() {
client = WebTestClient.bindToController(MyController.class).build();
}
#Test
public void getPages() throws Exception {
client.get().uri("/").exchange().expectStatus().isOk();
}
But I'm getting 404, seems it can't find my controller
You have to pass actual controller instance to bindToController method.
As you want to test mock environment, you'll need to mock your dependencies, for example using Mockito.
public class MyControllerReactiveTest {
private WebTestClient client;
#Before
public void setup() {
client = WebTestClient
.bindToController(new MyController(new MyService()))
.build();
}
#Test
public void getPages() throws Exception {
client.get()
.uri("/")
.exchange()
.expectStatus().isOk();
}
}
More test examples you can find here.
Also, I suggest switching to constructor-based DI.