在并行上下文中的 @WithMockUser

huangapple go评论82阅读模式
英文:

@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() // 注释掉这一行,它就会正常工作
            .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或任何线程时,它的行为是相同的。

那么,我该如何并行执行这个方法呢?

英文:

The following code is just a simplified version of what I want to achieve.

I am using @WithMockUser(username = "jane@no-domain.com", authorities = {"ROLE_ADMIN"}) to mock a user in my tests.

@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());
    }

}

When I execute the above code with .parallel() commented out, it works and prints out the following as intended:

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

... but when I add the .parallel() it throws a NullPointerException since somehow the mocked user is not present.

It behaves the same way when I use an ExecutorService or any threading of some sort.

So, how do I execute the method in parallel?

答案1

得分: 3

使用.parallel()将会启动多个线程并行运行流。根据教程中的描述:

> 当流以并行方式执行时,Java运行时会将流分成多个子流。聚合操作会并行迭代和处理这些子流,然后合并结果。

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

@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
英文:

The use of .parallel() will spawn multiple threads to run over the stream in parallel. From the tutorial on streams:

> 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.

So you need to make the children threads inherit the SecurityContextHolder from the local thread, for this you could use @PostConstruct as follows, (see more info 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());
    }

}

Output

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

huangapple
  • 本文由 发表于 2020年7月25日 16:24:48
  • 转载请务必保留本文链接:https://go.coder-hub.com/63086101.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定