Unit testing #PreAuthorized(hasRole) controller methods - spring

I'm trying to write unit test for this controller method
#PreAuthorize("hasRole(#d.code) or hasRole('ROLE_ALL_ACCESS')")
#RequestMapping(value = "{department}/{examination}", method = RequestMethod.GET)
public String show(ModelMap model, Principal principal, Locale locale, D d) {
model.addAttribute(service.getItem(d) //THIS CAUSES NULL ON TEST
.addAttribute(new DateTime())
.addAttribute(locale);
return "show";
I tried this
#Test (expected=AccessDeniedException.class)
public void testShowAccessDenied() {
super.simulateRole("ROLE_STUDENT");
controller.show(new ModelMap(), Helper.getTestPrincipal(), Locale.getDefault(), new D());
But it causes NullPointerException on the marked line, which means test is running the method instead of throwing AccessDeniedException based on the wrong role.
My super test class is
public class AuthorizeTestBase {
private Mockery jmock = new JUnit4Mockery();
private AuthenticationManager authenticationManager= jmock.mock(AuthenticationManager.class);
#Before
public void setUp() {
jmock.checking(new Expectations() {{ ignoring(anything()); }});
}
protected void simulateRole(String role) {
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
authorities.add(new SimpleGrantedAuthority(role));
PreAuthenticatedAuthenticationToken pat = new PreAuthenticatedAuthenticationToken(Helper.getTestPrincipal(), "", authorities);
SecurityContextHolder.getContext().setAuthentication(authenticationManager.authenticate(pat));
}
Helper.getTestPrincipal is
LdapPerson.Essence essence = new LdapPerson.Essence();
LdapPerson user = (LdapPerson) essence.createUserDetails();
return new PreAuthenticatedAuthenticationToken(user, "password");
I think I'm missing something on mocking authenticationManager. Help me!

The NPE is happeing when you try to call the service. Your class AuthorizeTestBase doesn't know about any spring beans service/dao etc . So it should load the spring wire configuration. The below is an example.
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextConfiguration(locations = {"classpath:/applicationContext-test.xml",
"classpath:/applicationContext-ABC-test.xml",
"classpath:/applicationContext-DEF-test.xml",
"classpath:/applicationContext-serviceGHI-test.xml",
"classpath:/applicationContext-securityHIJ-test.xml"
})
public class AuthorizeTestBase {
.....
}
Also please have a look into - Spring MVC Test Framework

Related

Error when mocking a repository in unit test of a spring webflux application (using JUnit and Mockito)?

I am yet to find a solution to this.
My Test class:
#WebFluxTest(controllers = {PatronController.class})
#Import({PatronService.class}) //to satisfy PatronController dependency.
#ExtendWith({PatronParameterResolver.class})
class PatronFunctionsSpec {
private Patron patron;
private Mono<Patron> patronMono;
private final PatronService patronService = Mockito.mock(PatronService.class);
#MockBean
private PatronRepository patronRepository;
#Autowired
private WebTestClient client;
#BeforeEach
void init(Patron injectedPatron) {
patron = injectedPatron;
patronMono = Mono.just(patron);
}
//Patron Story: patron wants to create an account with us
#Nested
#DisplayName("Creating a patron.")
class CreatingPatron {
#Test
#DisplayName("PatronService.create() returns success msg in Response obj after creating patron.")
void getResponseObjFromServiceCreate() {
Flux<Patron> patronFlux = Flux.from(patronMono);
Mockito.when(patronRepository.saveAll(patronMono)).thenReturn(patronFlux);
PatronService patronService = new PatronService(patronRepository);
Mono<Response> responseStream = patronService.create(Mono.just(patron));
Mono<Response> expectedResponseStream = Mono.just(new Response("Welcome, patron. Can't show emojis yet -- sorry."));
assertEquals(expectedResponseStream, responseStream);
}
}
}
See my PatronService class with its code:
#Service
public class PatronService {
private final PatronRepository patronRepository;
public PatronService(PatronRepository patronRepository) {
this.patronRepository = patronRepository;
}
/**
*
* persists patron via PatronRepo
*/
public Mono<Response> create(Mono<Patron> patronMono) {
patronRepository.saveAll(patronMono).subscribe();
return Mono.just(new Response("Welcome, patron. Can't find the emojis yet -- sorry."));
}
}
I am testing the PatronService's create(), so need to mock and stub PatronRepository and its function respectively. But the problem is: after running the test case, I get this exception:
java.lang.NullPointerException: Cannot invoke "reactor.core.publisher.Flux.subscribe()" because "patronFlux" is null
at com.budgit.service.PatronService.create(PatronService.java:26)
How can I fix this?

JPA AuditorAware not getting created for each user

I have implemented auditing using JPA auditing. My code looks like this:
#Configuration
#EnableJpaAuditing(auditorAwareRef = "auditorAware")
public class JpaConfiguration {
#Bean
#Scope(value= ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public AuditorAware<String> auditorAware() {
final String currentUser;
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if(null != authentication) {
currentUser = authentication.getName();
} else {
currentUser = null;
}
return () -> Optional.ofNullable(currentUser);
}
}
The issue I am facing is if I login with one user and perform some operation, it's working fine. But when I logout and login with another user, It's still using the last user only.
After debugging the code what I found is spring not creating bean of AuditorAware for each user. It's behaving like singleton bean. Even I specify the scope as prototype also, still it's behaving like singleton.
The AuditorAware is supposed to be a singleton. You should retrieve the current user, each time the AuditAware.getCurrentAuditor is called. Not just once.
Rewrite your code to something like this.
#Bean
public AuditorAware<String> auditorAware() {
return () -> getCurrentAuthentication().map(Authentication::getName());
}
private Optional<Authentication> getCurrentAuthentication() {
return Optional.ofNullable(SecurityContextHolder.getContext().getAuthentication());
}

spring boot restcontroller test returns null

I'm testing the "find" method of the controler that returns a "findById" but the return is always null.
My project is structured as follows:
I have a LegalPerson entity
A repository that extends a JpaRepository
And a service that "uses" the repository.
#SpringBootTest
#AutoConfigureMockMvc
#ExtendWith(SpringExtension.class)
class LegalPersonResourceTest {
#MockBean
private LegalPersonService service;
#Autowired
private MockMvc mvc;
#Test
void find() {
var localDate = LocalDate.of(1955, 10, 25);
List<Long> subsidiaries = new ArrayList<>() {{
add(10L);
add(20L);
}};
List<Long> phones = new ArrayList<>() {{
add(50L);
add(60L);
}};
var mockLP = LegalPerson.builder()
.id(1L)
.active(true)
.companyId(1L)
.tradeName("Test Company Trade Name")
.companyName("Test Company Company Name")
.email("test#com")
.cnpj("testCNPJ")
.stateRegistration("test state Registration")
.municipalRegistration("test Municipal Resgistration")
.openingDate(localDate)
.address(1L)
.companyType(CompanyEnum.HEADOFFICE)
.subsidiaries(subsidiaries)
.phones(phones)
.build();
Mockito.doReturn(mockLP).when(service).find(1L);
}
}
I wonder what I'm forgetting, or writing wrong.
EDITED 01 :
Mockito.when(this.service.find(ArgumentMatchers.eq(1L))).thenReturn(mockLP);
mvc.perform(MockMvcRequestBuilders.get("/api/clients/lp/{id}", 1L))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.content().contentType("application/json;charset=UTF-8"))
.andExpect(MockMvcResultMatchers.jsonPath("$.active", Matchers.is(true)));
It works perfectly.
But if I add
.andExpect(MockMvcResultMatchers.header().string(HttpHeaders.ETAG, "\"1\""))
return null.
You only are mocking the service, but no testing anything in this code, you may want to test the controller, something like this:
import static org.mockito.BDDMockito.given;
#Test
public void shouldGetAPerson() throws Exception {
//...
given(service.find(1L)).willReturn(mockLP);
mvc.perform(MockMvcRequestBuilders.get("/person/1")
.contentType("application/json"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.id", Matchers.containsString("1")));
}
Try with ArgumentMatchers
Mockito.when(this.service.find(ArgumentMatchers.eq(1L)).thenReturn(mockLP);

#WebMvcTest content is null

I've already read this Q&A but it didn't solve the problem. I'm using Spring Boot 1.4.2.RELEASE and I'm attempting to speed up my tests. Up to this point, I've used #SpringBootTest and I'm testing switching some of these simpler tests to #WebMvcTest.
My controller has the following method which is responding to GET requests.
public ResponseEntity<MappingJacksonValue> fetchOne(#PathVariable Long id, #RequestParam(value = "view", defaultValue = "summary", required = false) String view) throws NotFoundException {
Brand brand = this.brandService.findById(id);
if (brand == null) {
throw new NotFoundException("Brand Not Found");
}
MappingJacksonValue mappingJacksonValue = jsonView(view, brand);
return new ResponseEntity<>(mappingJacksonValue, HttpStatus.OK);
}
My test looks like this:
#RunWith(SpringRunner.class)
#WebMvcTest(BrandController.class)
public class BrandSimpleControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private BrandService brandService;
#Test
public void testExample() throws Exception {
Brand brand = new Brand(1l);
brand.setName("Test Name");
brand.setDateCreated(new Date());
brand.setLastUpdated(new Date());
when(this.brandService.findById(1l)).thenReturn(brand);
this.mockMvc.perform(get("/api/brands/1").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.name", is("Test Name")));
}
}
When I run this test, I get nothing back in the content. I'm not doing anything significantly different than this guide, so not sure what I'm missing.
I should note that using #SpringBootTest with the exact same controller works as expected.

Testing that delete is correctly rolled back with DataIntegrityViolationException junit, spring, #Transactional

I have a category -> subCategory -> products hierarchy in my application. If a subcategory has no products, you are allowed to delete it. If a subCategory has products, the DAO throws a DataIntegrityViolationException and the transaction should be rolled back.
In my tests, I have:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {TestTransactionManagement.class})
public class BusinessSubCategoryCRUDTest {
#Autowired
public void setCRUD(BusinessSubCategoryCRUD crud) {
this.crud = crud;
}
// #Transactional
#Test
public void testDeleteBusinessSubCategoryInUseCanNotBeDeleted() {
final long id = 1;
BusinessSubCategory subCategoryBeforeDelete =
crud.readBusinessSubCategory(id);
final int numCategoriesBeforeDelete =
subCategoryBeforeDelete.getBusinessCategories().size();
try {
crud.deleteBusinessSubCategory(
new BusinessSubCategory(id, ""));
} catch (DataIntegrityViolationException e) {
System.err.println(e);
}
BusinessSubCategory subCategoryAfterDeleteFails =
crud.readBusinessSubCategory(id);
// THIS next assertion is the source of my angst.
// At this point the the links to the categories will have been
// been deleted, an exception will have been thrown but the
// Transaction is not yet rolled back if the test case (or test
// class) is marked with #Transactional
assertEquals(
numCategoriesBeforeDelete,
subCategoryAfterDeleteFails.getBusinessCategories().size());
}
}
However, if I uncomment the #Transactional above #Test, it fails. I think the DAO is using the transaction from the #Test and so the transaction doesn't roll back until AFTER I check to be sure the transaction has been rolled back.
#Transactional(readOnly = false, propagation =
Propagation.REQUIRED)
public boolean deleteBusinessSubCategory(
BusinessSubCategory businessSubCategory) {
BeanPropertySqlParameterSource paramMap = new
BeanPropertySqlParameterSource(businessSubCategory);
namedJdbcTemplate.update(
DELETE_CATEGORY_SUB_CATEGORY_BY_ID_SQL,
paramMap);
return 0 != namedJdbcTemplate.update(
DELETE_SUB_CATEGORY_BY_ID_SQL,
paramMap);
}
So, how do I have the DAO code still inherit the transaction from the context it is running in (in production it inherits the transaction from the service it is running in) but still be able to test it. I want to put #Transactional on the entire test class, but that then leaves my test either failing or incomplete.
For completeness, here is my configuration class for the test.
#Configuration
#EnableTransactionManagement
public class TestTransactionManagement {
#Bean
public EmbeddedDatabase getDataSource() {
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
EmbeddedDatabase db = builder
.setType(EmbeddedDatabaseType.HSQL) //.H2 or .DERBY
.addScript("sql/create-db.sql")
.addScript("sql/create-test-data.sql")
.build();
return db;
}
#Bean
public DataSourceTransactionManager transactionManager() {
return new DataSourceTransactionManager(getDataSource());
}
#Bean
public BusinessSubCategoryCRUD getCRUD() {
return new BusinessSubCategoryCRUD(getDataSource());
}
}
The "solution" or workaround was to reset the database before each test. Then there was no need for an #Transactional on the test, the rollback could be tested, and the test suite ran slighly slower due to the additional database setup.
#Before
public void setUp() {
Connection conn = DataSourceUtils.getConnection(dataSource);
ScriptUtils.executeSqlScript(
conn, new ClassPathResource("sql/create-test-data.sql"));
DataSourceUtils.releaseConnection(conn, dataSource);
}

Resources