并行上下文中的@WithMockUser

@WithMockUser in a parallel context

下面的代码只是我想要实现的一个简化版本。

我正在使用 @WithMockUser(username = "jane@no-domain.com", authorities = {"ROLE_ADMIN"}) 在我的测试中模拟用户。

@RunWith(SpringRunner.class)
@SpringBootTest
@EnableAsync
@WithMockUser(username = "jane@no-domain.com", authorities = {"ROLE_ADMIN"})
public class NonTest {

    @Test
    public void test() {
        IntStream.range(1, 10)
            .parallel() // Comment this and it will work
            .forEach(value -> {
                getUser(value);
            });
    }

    public void getUser(int value) {
        System.out.println(value + ": " + ((User) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUsername());
    }

}

当我执行上面的代码并注释掉 .parallel() 时,它可以正常工作并按预期打印出以下内容:

1: jane@no-domain.com
2: jane@no-domain.com
3: jane@no-domain.com
4: jane@no-domain.com
5: jane@no-domain.com
6: jane@no-domain.com
7: jane@no-domain.com
8: jane@no-domain.com
9: jane@no-domain.com

...但是当我添加 .parallel() 它抛出一个 NullPointerException 因为不知何故模拟用户不存在。

当我使用 ExecutorService 或某种线程时,它的行为方式相同。

那么,如何并行执行该方法?

使用 .parallel() 将在流上并行生成多个线程 运行。来自 tutorial 上的流:

When a stream executes in parallel, the Java runtime partitions the stream into multiple substreams. Aggregate operations iterate over and process these substreams in parallel and then combine the results.

因此您需要让子线程从本地线程继承 SecurityContextHolder,为此您可以使用 @PostConstruct,如下所示,(请参阅更多信息 here):

@SpringBootTest
@EnableAsync
@WithMockUser(username = "jane@no-domain.com", authorities = {"ROLE_ADMIN"})
public class NonTest {

    @Test
    public void test() {
        IntStream.range(1, 10)
            .parallel()
            .forEach(this::getUser);
    }

    @PostConstruct
    void setGlobalSecurityContext() {
        SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
    }

    public void getUser(int value) {
        System.out.println(value + ": " + ((User) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUsername());
    }

}

输出

6: jane@no-domain.com
7: jane@no-domain.com
1: jane@no-domain.com
2: jane@no-domain.com
3: jane@no-domain.com
9: jane@no-domain.com
4: jane@no-domain.com
8: jane@no-domain.com
5: jane@no-domain.com