0

I am trying to wire up few tests. My doubt is regarding execution of Order of TestExecutionListener with parent class @Before and @After sequence of execution for currently running Test.

Example:

I have a class which setup some dummy user in table in test profile and deletes them. So i have created a base class for user related items. My Base class is as follows:

public abstract class BaseDataTest {
    @Autowired
    private UserRepository userRepository;

    private TestUserGenerator testUserGenerator;

    @Before
    public void setUp() {
        testUserGenerator = new TestUserGenerator(userRepository);
        testUserGenerator.createTestUsers();
    }

    @After
    public void cleanUp() {
        testUserGenerator.deleteTestUsers();
        testUserGenerator = null;
    }
}

I now have 2 scenarios:

  1. Just insert user and later fetch from database that the count is more and users are present in test database.
  2. I have to insert test users and later check for a particular user and fetch all the users from blocked table from current logged in user.

For scenario 1 which works fine since there is no TestExecutionListener annotation present on Test class so base class method setUp/cleanUp are called properly and my tests runs fine. My Test class is as below:

@RunWith(SpringRunner.class)
@SpringBootTest
@ActiveProfiles("test")
public class UserInsertTest extends BaseDataTest {

    @Autowired
    UserRepository userRepository;


    @Test
    public void testThatSystemHasUsers() {
        Assert.assertNotSame(userRepository.count(), 0);
    }

}

For scenario 2 which is not working as becuase i have TestExecutionListener with WithSecurityContextTestExecutionListener class as value, since my setUp method is not getting called and system has no users to prove authentication test is failing. My BlockUserTest class is as below:

@RunWith(SpringRunner.class)
@SpringBootTest
@ActiveProfiles("test")
@TestExecutionListeners(value = {WithSecurityContextTestExecutionListener.class})
public class BlockUserTest extends BaseDataTest {

    @Autowired
    private AppUserDetailsService userDetailsService;

    @Autowired
    private BlockService blockRepository;

    @Test
    @WithUserDetails(value = "sabrinahuff@comstruct.com")
    public void blockNoneReturnEmpty() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        User user = userDetailsService.loadUserByUsername(authentication.getName());
        List<Block> blockedByMe = blockRepository.getBlockedByMe(user.getId(), PageRequest.of(0, Integer.MAX_VALUE));

        Assert.assertNotNull(blockedByMe);
        //as i have not blocked anyone yet in test    
        Assert.assertSame(blockedByMe.size(), 0);
    }

}

I am getting

    java.lang.IllegalStateException: Unable to create SecurityContext using @org.springframework.security.test.context.support.WithUserDetails(value=sabrinahuff@comstruct.com, userDetailsServiceBeanName=, setupBefore=TEST_METHOD)

    at org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener.createTestSecurityContext(WithSecurityContextTestExecutionListener.java:126)
    at org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener.createTestSecurityContext(WithSecurityContextTestExecutionListener.java:96)
    at org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener.beforeTestMethod(WithSecurityContextTestExecutionListener.java:62)
    at org.springframework.test.context.TestContextManager.beforeTestMethod(TestContextManager.java:291)
    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74)
    at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Caused by: java.lang.NullPointerException
    at org.springframework.security.test.context.support.WithUserDetailsSecurityContextFactory.createSecurityContext(WithUserDetailsSecurityContextFactory.java:63)
    at org.springframework.security.test.context.support.WithUserDetailsSecurityContextFactory.createSecurityContext(WithUserDetailsSecurityContextFactory.java:44)
    at org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener.createTestSecurityContext(WithSecurityContextTestExecutionListener.java:123)
    ... 24 more


java.lang.NullPointerException
    at com.pkg.BaseDataTest.cleanUp(BaseDataTest.java:23)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:33)
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

2019-05-14 13:21:23.104  INFO 17612 --- [       Thread-2] o.s.s.concurrent.ThreadPoolTaskExecutor  : Shutting down ExecutorService 'applicationTaskExecutor'
2019-05-14 13:21:23.108  INFO 17612 --- [       Thread-2] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2019-05-14 13:21:23.109  INFO 17612 --- [       Thread-2] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown initiated...
2019-05-14 13:21:23.112  INFO 17612 --- [       Thread-2] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown completed.

Which seems valid as inside principal is is null, because there are no test users present in db and hence loadUserByUsername is returned with null value.

Method from org.springframework.security.test.context.support.WithUserDetailsSecurityContextFactory

public SecurityContext createSecurityContext(WithUserDetails withUser) {
        String beanName = withUser.userDetailsServiceBeanName();
        UserDetailsService userDetailsService = this.findUserDetailsService(beanName);
        String username = withUser.value();
        Assert.hasLength(username, "value() must be non empty String");
        UserDetails principal = userDetailsService.loadUserByUsername(username);
        Authentication authentication = new UsernamePasswordAuthenticationToken(principal, principal.getPassword(), principal.getAuthorities());
        SecurityContext context = SecurityContextHolder.createEmptyContext();
        context.setAuthentication(authentication);
        return context;
    }

Keeping TestExecutionListener and my test data generation how can i pass my block user test. Thank you!

Spring SecurityTest

@Test
    @WithUserDetails(value = "brysonhensley@yahoo.com")
    public void test() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        Assert.assertEquals(1, 1);
    }

This seems to be working and upon debugging, my authentication object is populated with meaningful values, so UserDetailsService bean name is picked up i think so.

silentsudo
  • 6,730
  • 6
  • 39
  • 81
  • Execution of `@Before` and `@After` has nothing to do with presence (or absence) of `TestExecutionListener`s. Also you don't need the specify it as the `WithSecurityContextTestExecutionListener` will be automatically applied. Also please add the full stacktrace instead of only the, partial, message. – M. Deinum May 14 '19 at 08:20
  • Ok, i have added full stacktrace, please take a look again. – silentsudo May 14 '19 at 08:54
  • You get a `NullPointerException`... probably originating from the fact you have a custom `UserDetailsService` with a different name. – M. Deinum May 14 '19 at 09:03
  • `public class AppUserDetailsService implements UserDetailsService{}` is my userdetails service i have updated my other test for spring authentication in code please take a look. – silentsudo May 14 '19 at 09:10
  • Again, it does a lookup by name `userDetailsService` if you have registered it differently, the name is different and no `UserDetailsService` will be found and hence lead to a `NullPointerException` either force the name to be `userDetailsService` or add the proper name to the `@WithUserDetails . – M. Deinum May 14 '19 at 09:12
  • I have added a test when authenticaion come true, can you please take a look again. – silentsudo May 14 '19 at 09:15
  • Again you get a `NullPointerException`, something is `null`that shouldn't be `null`. Maybe that user doesn't exist, or whatever. I cannot see or debug from here. – M. Deinum May 14 '19 at 09:17
  • Finally you got, user is not in db, because setUp method is never invoked, hence coming to the question, does order matters here? – silentsudo May 14 '19 at 09:19
  • And again, executing the setup method is not related to `TestExecutionListener`s. – M. Deinum May 14 '19 at 09:19
  • Ok, how can i prepopulate, database just before running security test such that userdetailservice would find users in database, am i doing something wrong? – silentsudo May 14 '19 at 09:23
  • May be i am running into this https://stackoverflow.com/questions/42466954/spring-security-junit-withuserdetails-for-user-created-in-before – silentsudo May 14 '19 at 09:37
  • Rather this answer (linked from the one you added) https://stackoverflow.com/questions/38275420/issue-with-withuserdetails-and-spring-boot-1-4-testentitymanager/38282258#38282258 is the solution and apparently this specific one, executes before the `@Before` method (The answer is by the giuy who wrote/maintains the Spring Test stuff :) ). So apparently I was wrong, for this particulair `TestExecutionListener`. – M. Deinum May 14 '19 at 09:41
  • Unfortunately i am not able to get it running :'( – silentsudo May 14 '19 at 09:58

0 Answers0