I have a respository method annotated with @Secured. I am trying to write a unit test for this method, but my test fails because I need authentication to call the method. The method itself, happens to be the save() method. The error I get when I call the method is:
org.springframework.security.authentication.AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext
I cannot test this method because it requires authentication, and I cannot save a user to authenticate against (I am using hsqldb) because I would need to call this method to save. Any advice on how to unit test a method annotated with @secured or how to mock the authentication.
It depends on what you want to test.
If you want to test business logic of your application without any security stuff, you can disable security altogether. Assuming that you use SpringJunit4Runner
, you can put security configuration into separate file and don't include it into @ContextConfiguration
.
If you want to test authorization (e.g. correct work of @Secured
annotations), you can access SecurityContext
directly, bypassing all authentication-related stuff (in this case you can put authentication configuration into separate file and don't load it as well):
All you need is to put an appropriate Authentication
object into SecurityContextHolder
:
@Test
public void myTest() {
login(...);
...
logout();
}
private void login(...) {
SecurityContextHolder.getContext().setAuthentication(
new UsernamePasswordAuthenticationToken(...)
)
}
private void logout() {
SecurityContextHolder.clearContext();
}
Finally, if you want to test authentication, you can run the test with the database containing test data.
a) This is not a unit test, it's an integration test. A unit test would be no problem.
b) I'd try to keep your test setups separated. Why not just deactive spring security in tests where you are not testing security-related features?
c) If all else fails: inject a JdbcTemplate
object and create the user data manulally in SQL during test setup. See Support classes for integration testing
I'd consider using an annotation such as @WithMockUser
to populate test security context with "personas" (test user with specific authorities like 'admin', 'salesman', etc.).
If @Secured
is on the repo, it must be because specs were stating something like "user must have this grant to query that", and so security should be unit tested too.
If UsernameAuthenticationToken
(what @WithMockUser
creates) doesn't meet your needs, then you might choose an other annotation, like this one I wrote, or even write one of your own creating the exact Authentication
implementation you want.
Libs here, sample result extracted from there
@RunWith(SpringRunner.class)
@Import(JwtAuthenticationTokenMessageServiceTests.TestConfig.class)
public class JwtAuthenticationTokenMessageServiceTests {
@Autowired
private MessageService<JwtAuthenticationToken> messageService;
@Test
@WithMockAuthentication(authType = JwtAuthenticationToken.class, authorities = "ROLE_AUTHORIZED_PERSONNEL")
public void secretWithScopeAuthorizedPersonnelAuthority() {
assertThat(messageService.getSecret()).isEqualTo("Secret message");
}
@TestConfiguration
@EnableGlobalMethodSecurity(prePostEnabled = true)
@Import({ JwtTestConf.class, JwtAuthenticationTokenServletApp.JwtAuthenticationTokenMessageService.class })
public static class TestConfig {
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With