【问题标题】:How to JUnit4-test Spring's el-Expressions on mocked request?如何根据模拟请求对 Spring el-Expressions 进行 JUnit 4 测试?
【发布时间】:2023-04-04 19:20:02
【问题描述】:

这是我所拥有的,但 Assert 总是引发 AsserationException:

@Configuration
public class Config
  @Bean
  public HttpServletRequest request(){
     HttpServleRequest mock = EasyMock.createMock(HttpServletRequest.class);
     EasyMock.expect(mock.getParameter("test")).andReturn("123").anyTimes();
     EasyMock.replay(mock);
     return mock;
  }

  @Bean
  MySerivce service(){
    return new MyService();
  }

  @Bean
  public ApplicationContextProvider context(){
    return new ApplicationContextProvider();
  }
}

这是服务MyService.java

public class MyService{
  public void assertHasRequest(@Value("#{request}") HttpServletRequest request)){
    assert request != null;
  }
}

测试:TestExpressions.java:

@RunWith(SpringJUnit4ClasssRunner.class)
@ContextConfiguration(classes=Config.class)
public class TestExpressions {

  @Autowired
  MyService service;

  @Test
  public void testService(){
    service.assertHasRequest(null);
  }
}

有什么想法吗?

【问题讨论】:

  • 我不太确定你想要达到什么目的,但它看起来很整洁。在我看来有几个潜在的问题: 1) 当从“request()”方法返回时,mock 不处于重播模式。我想你想打电话给'EasyMock.replay(mock);'在返回之前。 2)assert语句是检查提供的值不为null,但你直接调用它以null为参数。当然它会抛出一个断言错误。您确定要注入请求对象并用它调用方法吗?
  • @DanTemple 1) 听起来合法但没有任何改变,我将此添加到问题中。 2) 是的。
  • 酷,那么您可以按照我认为的测试类服务的相同流程进行操作。所以有一个名为 HttpServletRequest 类型的请求的变量,然后注释它@Autowired。然后你可以将它传递给你当前有 null 的 'assertHasRequest(request)' 方法。
  • @DanTemple 我在另一个测试中执行此操作,我喜欢测试@Value 而不是@Autowire
  • 好吧,我不确定这是否可能,但看看this example I found online 我想你想用@Component 标记MyService 类,然后把@ComponentScan 放在顶部你的配置类也是。虽然,我已经说过一些博客文章声称这种事情是不可能的。

标签: java spring junit4 easymock


【解决方案1】:

恐怕您不能直接在方法参数上使用 @Value 注释,这样它就可以用默认值替换参数,以防提供 null。

在您的情况下,@Value 注释与 AutowiredAnnotationBeanPostProcessor 一起使用,并且此处理器不会监视方法参数级别的 @Value 注释。

您可以改为将 @Value 放在方法级别,这将使 Spring 从上下文中注入 HttpServletRequest bean,但它不会在每个请求上都这样做:

public class MyService {

    private HttpServletRequest request;

    @Value("#{request}")
    public void assertHasRequest(HttpServletRequest request) {
        if (request != null) {
            this.request = request;
        }
        assert this.request != null;
    }

}

配置类:

@Configuration
public class Config {

    @Bean
    public HttpServletRequest request() {
        HttpServletRequest mock = EasyMock.createMock(HttpServletRequest.class);
        EasyMock.expect(mock.getParameter("test")).andReturn("123").anyTimes();
        EasyMock.replay(mock);
        return mock;
    }

    @Bean
    MyService service() {
        return new MyService();
    }

}

测试类:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=Config.class)
public class TestExpressions {

    @Autowired
    MyService service;

    @Test
    public void testService(){
        service.assertHasRequest(null);
    }
}

在 Spring MVC 控制器中使用 @Value 会有不同的行为。

【讨论】:

  • 我的测试失败了,你自己尝试成功了吗?
  • 是的,我做了,今天晚些时候我会把我的完整代码发给你。
  • 嘿,junit/spring 版本可以解决问题。你能告诉我版本号吗?
  • 应该是 Spring 4。
  • 我用的是3.2,好像是这个问题。
【解决方案2】:

@Value 不应该这样使用。它的主要目的是提供一种在 自动装配 发生时为注入字段指定默认值的方法。这一点很重要,因为当从代码中调用方法时,您似乎想使用@Value,而当您调用该方法时,自动装配已经发生,您无法使用@Value 更改已经自动装配的字段。 @Value 在实例创建时使用一次。

@Value 与您需要的东西一起使用的最简单方法是这样的:

public class MyService {

   @Value("#{request}")
   private HttpServletRequest request;

   public void assertHasRequest() {// no parameters here because they don't act on local field this.request
       assertNotNull(this.request);
   }
}

在上面的代码中,当 Spring 创建在您的 Config 类中指定的 MyService 实例时,它会检测到 @Value 的使用以及在创建 MyService 时(在您调用 assertHasRequest() 之前)它将注入它找到的request 实例。

让我再举一个例子:

public class MyService {

    private HttpServletRequest request;

    public void assertHasRequest() {
        assertNotNull(this.request);
    }

    @Value("#{request}")
    private void initialize(HttpServletRequest request) {
        System.out.println("initializing request");
        this.request = request;
    }
}

在此代码中,@Value 放置在方法 initialize() 上。当MyService 实例被创建时,Spring 容器会自动调用此方法,一次。这就像一个初始化方法,目的是设置字段request。如果需要,您可以从代码中调用该方法,但它不起作用。 @Value 的含义是让 Spring 在创建 bean 实例后知道该做什么。

对于@Value放在方法或构造函数的参数上,需要使用@Autowired

public class MyService {

    private HttpServletRequest request;

    public void assertHasRequest() {
        assertNotNull(this.request);
    }

    @Autowired
    private void initialize(@Value("#{request}") HttpServletRequest request) {
        System.out.println("initializing request: " + request);
        this.request = request;
    }
}

【讨论】:

  • 好的,在不使用自定义 AOP 注入值的情况下,是否有任何替代 @Value 的方法?
  • 没有。到目前为止,我已经看到了您的代码并询问了您要做什么,但我觉得对于我们俩来说事情仍然不清楚。您的代码显示HttpServletRequest,但其他任何内容都不能证明您希望在 Web 环境中进行测试。但是,如果您是,请查看 Spring 本身的 this testing class
  • 我最初的计划是在@Service-Class 上编写一个像void assertTestParameterIs123(@Value("${request.getParameter('test')}") String test) 这样的新测试。但我现在知道 - 没有自定义 AOP 切入点 - 这是没有希望的。
猜你喜欢
  • 1970-01-01
  • 2020-06-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-25
  • 1970-01-01
  • 2013-02-21
  • 1970-01-01
相关资源
最近更新 更多