10

我正在使用 Spring 4.16,并且我有我的 ValidationAspect,它验证方法参数并在出现问题时抛出 ValidationException。当我运行服务器并发送请求时会调用它,但来自测试时不会调用:

package com.example.movies.domain.aspect;
...
@Aspect
public class ValidationAspect {

    private final Validator validator;

    public ValidationAspect(final Validator validator) {
        this.validator = validator;
    }

    @Pointcut("execution(* com.example.movies.domain.feature..*.*(..))")
    private void selectAllFeatureMethods() {
    }

    @Pointcut("bean(*Service)")
    private void selectAllServiceBeanMethods() {
    }

    @Before("selectAllFeatureMethods() && selectAllServiceBeanMethods()")
    public synchronized void validate(JoinPoint joinPoint) {
         // Validates method arguments which are annotated with @Valid
    }
}

我在其中创建方面 bean 的配置文件

package com.example.movies.domain.config;
...
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class AspectsConfiguration {

    @Bean
    @Description("Hibernate validator. Used to validate request's input")
    public Validator validator() {
        ValidatorFactory validationFactory = Validation.buildDefaultValidatorFactory();
        return validationFactory.getValidator();
    }

    @Bean
    @Description("Method validation aspect")
    public ValidationAspect validationAspect() {
        return new ValidationAspect(this.validator());
    }
}

所以这是测试,它应该在进入 addSoftware 方法之前抛出 ValidationException,因为它是一个无效的软件对象。

@ContextConfiguration
@ComponentScan(basePackages = {"com.example.movies.domain"})
public class SoftwareServiceTests {
    private static final Logger LOGGER = LoggerFactory.getLogger(SoftwareServiceTests.class.getName());

    private SoftwareService softwareService;
    @Mock
    private SoftwareDAO dao;
    @Mock
    private MapperFacade mapper;

    @Before
    public void init() {
        MockitoAnnotations.initMocks(this);
        this.softwareService = new SoftwareServiceImpl(this.dao);
        ((SoftwareServiceImpl) this.softwareService).setMapper(this.mapper);

        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SoftwareServiceTests.class);
        ctx.getBeanFactory().registerSingleton("mockedSoftwareService", this.softwareService);
        this.softwareService = (SoftwareService) ctx.getBean("mockedSoftwareService");

    }

    @Test(expected = ValidationException.class)
    public void testAddInvalidSoftware() throws ValidationException {
        LOGGER.info("Testing add invalid software");
        SoftwareObject softwareObject = new SoftwareObject();
        softwareObject.setName(null);
        softwareObject.setType(null);

        this.softwareService.addSoftware(softwareObject); // Is getting inside the method without beeing validated so doesn't throws ValidationException and test fails
    }
}

如果我运行该服务并从发布请求中添加此无效用户,则会抛出应有的 ValidationException。但由于某种原因,它永远不会从测试层执行 ValidationAspect 方法

还有我的服务

package com.example.movies.domain.feature.software.service;
...
@Service("softwareService")
public class SoftwareServiceImpl
    implements SoftwareService {

    @Override
    public SoftwareObject addSoftware(@Valid SoftwareObject software) {
         // If gets into this method then software has to be valid (has been validated by ValidationAspect since is annotated with @Valid)
         // ...
    }
}

我不明白为什么不调用方面,因为 mockedSoftwareService bean 位于功能包中,并且 bean 名称以“Service”结尾,因此它满足这两个条件。您对可能发生的事情有任何想法吗?提前致谢


编辑

@Service("softwareService")
public class SoftwareServiceImpl
    implements SoftwareService {

    private static final Logger LOGGER = LoggerFactory.getLogger(SoftwareServiceImpl.class.getName());

    private SoftwareDAO dao;
    private MapperFacade mapper;

    @Autowired
    private SoftwareCriteriaSupport criteriaSupport;

    @Autowired
    private SoftwareDefaultValuesLoader defaultValuesLoader;

    @Autowired
    public SoftwareServiceImpl(SoftwareDAO dao) {
        this.dao = dao;
    }

    @Autowired
    @Qualifier("domainMapper")
    public void setMapper(MapperFacade mapper) {
        this.mapper = mapper;
    }

   // other methods

}
4

4 回答 4

8

不确定您要做什么,但是您@ContextConfiguration没有用,因为您没有使用 Spring Test 来运行测试(这将需要 Spring Test 中的一个@RunWith或一个超类)。

接下来,您将添加一个已经完全模拟和配置的单例(这是上下文假设的)。我强烈建议使用 Spring 而不是解决它。

首先在您的测试类中创建一个配置进行测试,该配置应该进行扫描并注册模拟 bean。其次使用 Spring Test 运行您的测试。

@ContextConfiguration
public class SoftwareServiceTests extends AbstractJUnit4SpringContextTests {
    private static final Logger LOGGER = LoggerFactory.getLogger(SoftwareServiceTests.class.getName());

    @Autowired
    private SoftwareService softwareService;

    @Test(expected = ValidationException.class)
    public void testAddInvalidSoftware() throws ValidationException {
        LOGGER.info("Testing add invalid software");
        SoftwareObject softwareObject = new SoftwareObject();
        softwareObject.setName(null);
        softwareObject.setType(null);

        this.softwareService.addSoftware(softwareObject);
    }

    @Configuration
    @Import(AspectsConfiguration.class)
    public static class TestConfiguration {

        @Bean
        public SoftwareDAO softwareDao() {
            return Mockito.mock(SoftwareDAO.class);
        }

        @Bean
        public MapperFacade domainMapper() {
            return Mockito.mock(MapperFacade.class)
        }

        @Bean
        public SoftwareService softwareService() {
            SoftwareServiceImpl service = new SoftwareServiceImpl(softwareDao())
            return service;
        }

    }
}
于 2015-06-12T07:30:06.903 回答
7

了解 Spring AOP 是如何工作的很好。如果 Spring 托管的 bean 符合任何方面的条件(每个方面一个代理),它就会被包装在一个代理(或几个)中。

通常,Spring 使用该接口来创建代理,尽管它可以使用诸如 cglib 之类的库来处理常规类。在您的服务的情况下,这意味着 Spring 创建的实现实例被包装在一个代理中,该代理处理方法验证的方面调用。

现在您的测试手动创建了 SoftwareServiceImpl 实例,因此它不是 Spring 托管的 bean,因此 Spring 没有机会将其包装在代理中以能够使用您创建的方面。

您应该使用 Spring 来管理 bean 以使方面工作。

于 2015-06-14T20:50:57.200 回答
1

您需要使用 srping 运行:

@EnableAspectJAutoProxy
@RunWith(SpringJUnit4ClassRunner.class)
public class MyControllerTest {

}
于 2021-08-05T02:55:42.820 回答
0

确实有两件重要的事情需要意识到:

1) 对象树的根必须由应用程序上下文中注册的扫描对象来解析。如果你 new() 它,AOP 注释不可能被解析。

2)注解和AOP方面类需要注册。

广告 1) @Autowire 你的根对象就可以了

广告 2) 确保 @Component 使用正确的过滤器:@Component() 或 @Component("your full namespace package filters")

查看:

    @Bean
    public CommandLineRunner commandLineRunner(ApplicationContext ctx) 
    {
        return args -> 
        {
            log.debug("Let's inspect the beans provided by Spring Boot:");

            List<String> beanNames = Arrays.asList(ctx.getBeanDefinitionNames());
            Assert.isTrue( beanNames.contains("yourAspectClassName"));

        };
    }
于 2019-12-01T19:56:33.963 回答