You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何针对已认证与未认证用户重复执行Spring Security测试?

实现OAuth2接口的双场景自动测试(认证/未认证)

针对你的Spring Security OAuth2接口测试需求,以下两种方案可以让测试自动执行已认证和未认证两个场景:


方案一:JUnit 5参数化测试(快速实现)

利用JUnit 5的参数化测试特性,直接为每个测试方法注入认证场景参数,分场景执行断言。

1. 引入依赖(若未添加)

如果使用Maven,确保pom.xml中包含JUnit 5参数化依赖:

<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-params</artifactId>
    <scope>test</scope>
</dependency>

2. 定义认证场景枚举

enum AuthScenario {
    // 已认证场景
    AUTHENTICATED,
    // 未认证场景
    UNAUTHENTICATED
}

3. 改造测试类

将原有测试方法改为参数化测试,根据场景设置请求并执行对应断言:

import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.oauth2Login;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

public class HelloControllerTests {

    @Autowired 
    private MockMvc mockMvc;

    @ParameterizedTest
    @EnumSource(AuthScenario.class)
    public void shouldReturnWorld(AuthScenario scenario) throws Exception {
        var request = get("/api/hello");
        
        // 已认证场景添加oauth2Login处理器
        if (scenario == AuthScenario.AUTHENTICATED) {
            request = request.with(oauth2Login());
        }
        
        var result = mockMvc.perform(request);
        
        // 分场景断言
        if (scenario == AuthScenario.AUTHENTICATED) {
            result.andExpect(status().isOk())
                  .andExpect(content().string(containsString("Hello, World!")));
        } else {
            result.andExpect(status().isForbidden())
                  .andExpect(content().string(containsString("通用错误信息")));
        }
    }

    @ParameterizedTest
    @EnumSource(AuthScenario.class)
    public void shouldReturnBob(AuthScenario scenario) throws Exception {
        var request = get("/api/hello").param("name", "bob");
        
        if (scenario == AuthScenario.AUTHENTICATED) {
            request = request.with(oauth2Login());
        }
        
        var result = mockMvc.perform(request);
        
        if (scenario == AuthScenario.AUTHENTICATED) {
            result.andExpect(status().isOk())
                  .andExpect(content().string(containsString("Hello, Bob")));
        } else {
            result.andExpect(status().isForbidden())
                  .andExpect(content().string(containsString("通用错误信息")));
        }
    }
}

方案二:自定义注解+测试模板(减少重复代码)

如果有大量测试方法需要复用双场景逻辑,可以自定义注解,结合JUnit 5的测试模板扩展,统一管理场景执行逻辑。

1. 自定义注解

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.junit.jupiter.api.TestTemplate;
import org.junit.jupiter.api.extension.ExtendWith;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@TestTemplate
@ExtendWith(AuthScenarioExtension.class)
public @interface EnsureAuthenticated {
}

2. 实现扩展类

这个类负责提供双场景的测试上下文,并传递场景参数:

import org.junit.jupiter.api.extension.*;
import java.util.List;
import java.util.stream.Stream;

public class AuthScenarioExtension implements TestTemplateInvocationContextProvider {

    @Override
    public boolean supportsTestTemplate(ExtensionContext context) {
        return context.getTestMethod()
                .map(method -> method.isAnnotationPresent(EnsureAuthenticated.class))
                .orElse(false);
    }

    @Override
    public Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContexts(ExtensionContext context) {
        return Stream.of(
                createContext(AuthScenario.AUTHENTICATED),
                createContext(AuthScenario.UNAUTHENTICATED)
        );
    }

    private TestTemplateInvocationContext createContext(AuthScenario scenario) {
        return new TestTemplateInvocationContext() {
            @Override
            public String getDisplayName(int invocationIndex) {
                return scenario.name();
            }

            @Override
            public List<Extension> getAdditionalExtensions() {
                return List.of(new ParameterResolver() {
                    @Override
                    public boolean supportsParameter(ParameterContext paramCtx, ExtensionContext extCtx) {
                        return paramCtx.getParameter().getType() == AuthScenario.class;
                    }

                    @Override
                    public Object resolveParameter(ParameterContext paramCtx, ExtensionContext extCtx) {
                        return scenario;
                    }
                });
            }
        };
    }
}

3. 使用自定义注解改造测试类

只需给测试方法标记@EnsureAuthenticated,即可自动执行双场景测试:

@EnsureAuthenticated
public void shouldReturnWorld(AuthScenario scenario) throws Exception {
    // 逻辑和参数化测试中的一致,省略重复代码
}

@EnsureAuthenticated
public void shouldReturnBob(AuthScenario scenario) throws Exception {
    // 逻辑和参数化测试中的一致,省略重复代码
}

注意事项

  • 未认证场景的状态码:你的过滤器链配置/api/**需要认证,未认证访问默认返回403(Forbidden),若实际返回401(Unauthorized),需调整断言中的status().isForbidden()为status().isUnauthorized()。
  • 错误信息断言:如果接口返回JSON格式错误信息,建议使用jsonPath断言,例如andExpect(jsonPath("$.message").value("通用错误信息"))。

内容的提问来源于stack exchange,提问作者udduu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 03:04:51