当前位置: 首页 > 知识库问答 >
问题:

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

陆飞鸿
2023-03-14

我创建了一个之前的建议,使用的是ASHOJ:

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

spring配置文件:

<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时。执行(…)它应该拦截方法,抛出异常并返回401个未经授权的代码。

当然它不工作,我试图调试测试和:

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

我怎么能注入模拟实例的用户数据库访问提供程序到ValidSessionChecker?

共有1个答案

白越
2023-03-14

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

测试配置。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>

然后在测试中将行为注入到mock中:

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

这应该干净利落地工作。

 类似资料:
  • 我找到了一些答案:https://stackoverflow.com/a/21218921/2754014 关于依赖注入。没有任何注释,如,或。让我们假设此示例没有任何 XML 配置 的 bean (除了简单

  • 问题内容: 我的测试课: 我在行上得到一个空指针异常: 在精确的给出空指针异常 如何使Junit类中的beanObject字段自动装配成为可能,以便可以使用“ BeanClass”类中的方法? 从评论中复制: 用简单的术语来说.. beanClass是具有某些方法的接口..我用 注释标记了该beanClass。.banClass 是由具有方法实现的beanClassImpl类实现的。.我需要在我的

  • 例如,我有handler: 为了测试它,我编写了以下测试: 我可以使用模拟变量someCount。我可以用Mockito注释来模拟它吗?

  • 对于用@X注释的类中的方法或用@X注释的方法,我需要一个切入点。我还需要注释对象。如果类和方法都被注释,我更喜欢将方法注释作为参数。 我尝试了以下操作,这会产生“绑定不一致”的警告。(为什么不直接将其设置为null?) 以下内容创建了“穿过切入点中的“||”的参数x的不明确绑定”警告。(在我看来,这不一定有意义:为什么不绑定第一个短路评估?) 如果存在类和方法注释,则将前面的尝试拆分为两个,自然会

  • 可以模拟抽象类,也可以使用Mockito注释将模拟类注入抽象类。我现在有以下情况: 我想在AbstractClassUnderTest上使用类似@InjectMocks的东西,但它不能与@Mock结合使用。Powermock的Whitebox的当前情况是有效的,但我很好奇是否可以通过注释来解决它。我找不到任何解决方案或示例。 (我知道对测试抽象类的反对意见,我个人更愿意测试一个具体的实现,只使用@

  • 我一直在思考编译时评估注释值的Java特性,它似乎真的很难外部化注释值。 但是,我不确定这是否真的是不可能的,所以我非常感谢对此的任何建议或明确的答案。 更重要的是,我试图将控制Spring中预定方法调用之间延迟的注释值外部化,例如: 假设位于类路径上,并且包含属性键及其相应的长整型值。 现在,这段代码有明显的编译错误,因为我们试图将值赋给变量,但这是强制性的,因为我们不能将变量赋给注释值,除非它

  • 和和注释之间有什么区别? 我们应该在什么时候使用它们每一个?

  • 我正在使用注释处理器来处理方法参数的注释。 用于参数的注释类型有一个注释@参数 现在,当注释处理器运行时,我想检查参数注释()是否有参数注释。我通过执行以下代码来实现这一点。 由于某种原因,arg始终为空。是否有注释未返回的原因?