Spring:无法将模拟注入到使用 @Aspect 注释的类中

2023-12-15

我使用 AspectJ 创建了一个 Before 建议:

package test.accesscontrol.permissionchecker;

import test.accesscontrol.database.SessionExpiredException;
import test.database.UsersDatabaseAccessProvider;
import test.common.constants.GlobalConstants;
import test.common.model.AbstractRequest;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.beans.factory.annotation.Autowired;

@Aspect
public class ValidSessionChecker {
    private static final int REQUEST_PARAMETER_ARGUMENT_POSITION = GlobalConstants.ZERO;

    private UsersDatabaseAccessProvider usersDatabaseAccessProvider;

    @Autowired
    public ValidSessionChecker(UsersDatabaseAccessProvider usersDatabaseAccessProvider) {
        this.usersDatabaseAccessProvider = usersDatabaseAccessProvider;
    }

    @Before("@annotation(test.accesscontrol.permissionchecker.ValidSessionRequired)")
    public void before(JoinPoint joinPoint) throws Throwable {
        Object requestParameterObject = joinPoint.getArgs()[REQUEST_PARAMETER_ARGUMENT_POSITION];
        AbstractRequest requestParameter = (AbstractRequest) requestParameterObject;

        String sessionID = requestParameter.getSessionId();
        if(!usersDatabaseAccessProvider.sessionNotExpired(sessionID))
            throw new SessionExpiredException(String.format("Session expired: %s", sessionID));
    }
}

和一个测试类:

package test.accesscontrol;

import test.accesscontrol.database.UsersDatabaseAccessProvider;
import test.accesscontrol.permissionchecker.ValidSessionChecker;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;

import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.*;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration("file:src/main/webapp/WEB-INF/mvc-dispatcher-servlet.xml")
public class AccessControlControllerTestsWithInjectedMocks {

    @Autowired
    private org.springframework.web.context.WebApplicationContext wac;

    private MockMvc mockMvc;

    @Mock
    UsersDatabaseAccessProvider usersDatabaseAccessProvider;

    @InjectMocks
    ValidSessionChecker validSessionChecker;

    @InjectMocks
    AccessControlController accessControlController;

    @Before
    public void before() throws Throwable {
        //given
        MockitoAnnotations.initMocks(this);

        when(usersDatabaseAccessProvider.sessionNotExpired("123456")).thenReturn(Boolean.FALSE);

        this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
    }

    @Test
    public void changePassword_shouldReturnUnauthorizedHttpCodeWhenSessionIsExpired() throws Exception {
        //when
        ResultActions results = mockMvc.perform(
                post("/accesscontrol/changePassword")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content("{\"sessionId\":\"123456\", \"oldPassword\":\"password\", \"newPassword\":\"newPassword\"}")
        );

        //then
        results.andExpect(status().isUnauthorized());
        verify(usersDatabaseAccessProvider, never()).getSessionOwner(anyString());
        verify(usersDatabaseAccessProvider, never()).isCurrentPasswordValid(anyString(), anyString());
        verify(usersDatabaseAccessProvider, never()).setNewPassword(anyString(), anyString());
    }
}

弹簧配置文件:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

    <mvc:annotation-driven />
    <aop:aspectj-autoproxy />

    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/pages/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

    <bean class="org.springframework.context.support.ResourceBundleMessageSource"
          id="messageSource">
        <property name="basename" value="messages" />
    </bean>

    <bean id="usersDatabaseAccessProvider" class="test.accesscontrol.database.UsersDatabaseAccessProvider"/>

    <bean id="accessControlController" class="test.accesscontrol.AccessControlController">
        <property name="sessionExpirationTimeInSeconds" value="600"/>
    </bean>

    <bean id="validSessionChecker" class="test.accesscontrol.permissionchecker.ValidSessionChecker" />

    <bean id="timeDispatcher" class="test.utils.time.TimeDispatcher" scope="singleton" />

</beans>

门禁控制器

@Controller
@RequestMapping("/accesscontrol")
public class AccessControlController {
...

    @RequestMapping(value = "changePassword", method = RequestMethod.POST,
        consumes = MediaType.APPLICATION_JSON_VALUE)
    @ValidSessionRequired
    public ResponseEntity<Void> changePassword(@Valid @RequestBody ChangePasswordRequest request) throws OperationForbiddenException {
        String sessionId = request.getSessionId();
        String userEmailAddress = usersDatabaseAccessProvider.getSessionOwner(sessionId);
        String currentPassword = request.getOldPassword();

        this.ensureThatCurrentPasswordIsValid(userEmailAddress, currentPassword);

        usersDatabaseAccessProvider.setNewPassword(userEmailAddress, request.getNewPassword());
        return new ResponseEntity<Void>(HttpStatus.OK);
    }

    @ExceptionHandler({SessionExpiredException.class})
    public ResponseEntity<Void> handleSessionExpiredException(Exception ex) {
        return new ResponseEntity<Void>(HttpStatus.UNAUTHORIZED);
    }
}

当我调用mockMvc.perform(...)时,它应该拦截方法,抛出异常并返回401未经授权的代码。

当然它不起作用,我尝试调试测试并:

  1. 在 MockitoAnnotations.initMocks(this); 之后 有一个 UsersDatabaseAccessProvider 实例(模拟)分配给所有类(ValidSessionChecker、AccessControlController 和 AccessControlControllerTestsWithInjectedMocks)中的字段。
  2. 但是,当执行 before(JoinPoint joinPoint) 时,ValidSessionChecker 对象中的 usersDatabaseAccessProvider 字段包含 UsersDatabaseAccessProvider 的不同实例(ValidSessionChecker 对象也不同,它不是模拟版本)。

如何将 UsersDatabaseAccessProvider 的模拟实例注入 ValidSessionChecker 中?


这里的问题是你的 Mock 实例和ValidSessionChecker不是Spring bean,因此没有被连接到ValidSessionChecker由 Spring 管理。要制作模拟 Spring bean,更好的方法可能是创建另一个 bean 定义文件,该文件扩展基本配置文件中定义的 bean 并添加模拟:

测试配置.xml:

<beans...>
    <import resource="base-springmvc-config.xml"/>
    <beans:bean name="usersDatabaseAccessProvider" factory-method="mock"    class="org.mockito.Mockito">
    <beans:constructor-arg value="..UsersDatabaseAccessProvider"></beans:constructor-arg>
</beans:bean>

然后在您的测试中将行为注入模拟中:

public class AccessControlControllerTestsWithInjectedMocks {

    @Autowired
    private org.springframework.web.context.WebApplicationContext wac;

    private MockMvc mockMvc;

    @Autowired
    UsersDatabaseAccessProvider usersDatabaseAccessProvider;

    @Autowired
    ValidSessionChecker validSessionChecker;

    ....

    @Before
    public void before() throws Throwable {
        //given
        MockitoAnnotations.initMocks(this);

        when(usersDatabaseAccessProvider.sessionNotExpired("123456")).thenReturn(Boolean.FALSE);

        this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
    }

这应该可以正常工作。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Spring:无法将模拟注入到使用 @Aspect 注释的类中 的相关文章

随机推荐

  • jqgrid 省略号

    在 jqGrid 中 如果文本不适合并被截断 是否有一种本机方法可以在列末尾显示 我看到有一个 ui ellipsis 类 但我很困惑 如果文本被截断 它是否会自动添加 以及一旦调整列大小 它是否会自动消失 您可以使用以下CSS解决该问题
  • 使用 cURL 登录远程网页后,如何访问另一个网页并将其作为字符串返回?

    好吧 我对 cURL 还很陌生 我已经成功使用 cURL 登录网页并发送 POST 数据 但是一旦登录 我希望它能够在同一页面下加载各种网页SESSION并将数据提取为字符串 以便我可以检查网页是否包含特定字符串 我该怎么做呢 例如 它登录
  • SQL查询根据特定条件对分组进行计数

    我的表数据 id fieldId Name Text 1 101 name1 a1 2 102 name2 a2 3 101 name1 a1 4 103 name3 a2 5 102 name2 a3 6 101 name1 c1 7 1
  • 如何判断两个对象的类型是否兼容?

    我有一个通用函数 我想知道如何编写 List
  • 导入 Excel 数据似乎随机给出空值

    使用 SSIS for Visual Studio 2017 进行某些 Excel 文件导入 我创建了一个包含多个循环容器的包 这些循环容器调用特定的包来处理某些文件 我在执行一个特定包时遇到问题 因为它似乎随机决定每个 Excel 文件的
  • 如何居中对齐SPAN标签中包裹的img?

    我正在尝试居中对齐包裹在 span 但我这样做时遇到困难 我已将 CSS 和 HTML 上传到 jsfiddle http jsfiddle net 7nHhu 1 我试图让图像以 块 样式与内容居中对齐 即其上方和下方的所有文本 而不是向
  • 在 CoreBuild 之前运行目标?

    我正在添加自定义 tt模板生成目标到我的项目之前运行CoreBuild 似乎有两种方法可以做到这一点
  • UIApperance和各种崩溃

    我在自定义我的应用程序时感到非常沮丧 我已经创建并设计了几乎整个应用程序的样式 包括导航栏 工具栏 tabBar 等 但每次 MFMailComposeViewController MFMessageComposerViewControll
  • 正则表达式查找特定长度的数字,但可以包含除其之前或之后的数字之外的任何字符

    我正在尝试制定一个正则表达式模式来搜索字符串中的 12 位数字 该数字可以在我要查找的字符的前面或后面包含任意数量的其他字符 但不能是数字 到目前为止我已经 0 9 12 它可以正确找到 12 位数字 但它也会匹配字符串中的 13 位数字
  • (C++) 与命名空间链接会导致重复符号错误

    在过去的几天里 我一直在尝试弄清楚如何链接我一直在从事的 CLI 游戏项目的文件 该项目有两部分 客户端代码和服务器代码 客户需要我制作的两个库 第一个是通用游戏板 它分为 GameEngine h 和 GameEngine cpp 头文件
  • 从 NodeJS 中的 get 请求流式传输 axios 响应

    我正在寻找在节点程序中使用 axios 向方法 myMethod 发送 返回 可读流 gt 我想将响应流式传输到可用于发送给 myMethod 调用者的 ReadableStream This code does nt work but t
  • 使用 d3.json 从 PHP 输出 JSON

    我创建了一个 PHP 文件来查询 JSON 输出 特定过滤器 testPHP php number 123 的 PHP 文件的 JSON 输出是 source AB target AC type true source UB target
  • 我可以添加自定义 modbar 按钮吗?

    我有一个自动缩放 y 轴的函数 它接收图形和重新布局数据对象 然后输出符合我预先确定的某些标准的 y 轴 我想在 python 上的 dash 中添加一个新的 modbar 按钮 该按钮会在我点击它时触发该函数并更新图形 那可能吗 一个简单
  • 如何减少Python中大列表使用的内存

    我正在编写一个程序 它工作正常 但是当它将数据库 100MB 文本文件 加载到列表时 它的内存使用量变成 700 800MB 用于将文件加载到列表的代码 database db open database db hdb dbcontent
  • 使用 BERT (TF 1.x) 保存的模型执行推理

    我被困在一行代码结果整个周末一个项目都被搁置了 我正在开发一个使用 BERT 进行句子分类的项目 我已经成功训练了模型 并且可以使用 run classifier py 中的示例代码来测试结果 我可以使用此示例代码导出模型 该代码已多次重新
  • 这是 GAC 的所在地吗?

    这是 GAC 的所在地吗 C Windows Microsoft NET assembly GAC MSIL 如果是这样 那么什么是C Windows assembly 答案取决于所使用的 NET Framework 的版本 多个版本可以并
  • 将设备指针传递给 cublasSnrm2 时出现分段错误

    下面的 cublas 代码给了我们错误 core dumped while being at cublasSnrm2 handle row dy incy de 你能给一些建议吗 main cu include
  • 如何从 MySQL 表中获取所有数据组合? [复制]

    这个问题在这里已经有答案了 在过去的五个小时里 我一直试图从表格中获取每种产品选项组合 但现在我完全陷入困境 我有一个表 其中包含如下数据 简化 CREATE TABLE assigned options option id int 10
  • 如何将 C 文件编译为可执行 (.exe) 文件?

    我不确定在 Windows 7 下使用 Cygwin 将 C 文件编译为可执行文件 谁能告诉我该怎么做吗 我读过一些教程但仍然不明白 我知道我需要一个Makefile 但是我应该在其中写入什么才能在编译过程后获得可执行文件呢 一开始我会说安
  • Spring:无法将模拟注入到使用 @Aspect 注释的类中

    我使用 AspectJ 创建了一个 Before 建议 package test accesscontrol permissionchecker import test accesscontrol database SessionExpir