【问题标题】:SpyBean not being injected everywhereSpyBean 没有被到处注入
【发布时间】:2018-09-10 19:09:35
【问题描述】:

我很难将 spy bean 放入我的 ApplicationContext。我有一个名为 utilities 的 bean,类型为 Utilities

@Component("utilities")
public class Utilities {

<snip>

    /**
     * Returns a random int. This is provided mostly for testing mock-ability
     *
     * @return a random integer
     */
    public int getRandom() {
        return (int) (Math.random() * Integer.MAX_VALUE);
    }
}

它在我的 Spring 集成流程间接引用的类中使用。

然后我有这个木星测试:

@TestInstance(Lifecycle.PER_CLASS)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@ExtendWith(SpringExtension.class)
@ContextConfiguration( classes = {
    XmlLocations.class,
    VisitorManager.class,
    Utilities.class,
    UnixTimeChannel.class
})
@WebMvcTest
//@TestExecutionListeners( { MockitoTestExecutionListener.class })
public class FullIntegrationTest {

    @Autowired
    private MockMvc mvc;

    @SpyBean
    private Utilities utilities;

    private ClientAndServer mockServer;

    private static final int MOCK_SERVER_PORT = 9089;

    @BeforeAll
    public void setUpBeforeClass() {

        Mockito.when(utilities.getRandom()).thenReturn(Integer.MAX_VALUE);

        mockServer = ClientAndServer.startClientAndServer(MOCK_SERVER_PORT);
        RestAssuredMockMvc.mockMvc(mvc);
        (new MockServerPingInit()).initializeExpectations(mockServer);
        (new MockServerFullIntegrationInit()).initializeExpectations(mockServer);
    }

    @Test
    public void t00200_IncomingMessage() {

        RestAssuredMockMvc.given()
            .queryParam("example", "example")
            .when()
            .request("POST", "/api/v1/incoming")
            .then()
            .statusCode(equalTo(200));
    }

<snip>

但即使我创建了 spy bean 并在其上使用了 when/thenReturn,它也不会漂浮到我的应用程序上下文中等待被调用并返回它的模拟随机值。

我知道方法 utility.getRandom() 被调用,因为我可以在它上面放置一个断点并调试测试,它会命中 getRandom 方法,但是当我尝试添加一个如上所示的间谍 bean 并模拟时出 getRandom 返回一个固定值来测试断点仍然命中,所以我可以告诉真正的方法不是模拟被调用。

我也尝试将 when/thenReturn 放入测试中,以防它为时过早,但它没有帮助。

显然我做错了什么,可能在概念上是错误的。哈!

【问题讨论】:

  • 如果从上下文配置中删除 Utilities.class 会怎样?然后如果@SpyBean 不起作用,您应该会看到应用程序上下文创建失败。
  • 因为在 spring 集成流程中请求了实用程序 bean,所以在一个表达式中,我得到了这个:org.springframework.messaging.MessageHandlingException:表达式评估失败
  • 请展示您如何在集成流程中使用Utilities,并与我们分享整个堆栈跟踪。
  • 我有同样的问题,我认为这是因为 beforeAll (应该是静态的)在实例尚未创建并且 spybean 是实例字段时触发。如果我想要 beforeAll 注释,我仍然不知道如何解决这个问题

标签: java spring mockito spring-integration


【解决方案1】:

我试图用最少的配置重现你的问题:

@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = {Ctx.class})
public class XTest {

  @SpyBean
  private Random random1;

  @Autowired private Supplier<Integer> intSupplier;

  @Test
  public void test() {
    Mockito.when(random1.nextInt()).thenReturn(Integer.MAX_VALUE);
    int i = intSupplier.get();
    System.out.println("i=" + i);
  }

  @Configuration
  public static class Ctx {

    @Bean
    static Random random1() {
      return ThreadLocalRandom.current();
    }

    @Bean
    static Supplier<Integer> intSupplier(Random random1) {
      return random1::nextInt;
    }
  }
}

正如预期的那样,它会打印出来

i=2147483647

那么,您的运行时配置一定有问题...您能分享一下吗?我猜 spring-integration 正在使用另一个 ApplicationContext。我知道这不是答案,如果没有帮助,我会删除它。

【讨论】:

    【解决方案2】:

    好的,谢谢大家的帮助。由于我在下面发现的内容,我认为发布配置和流程无济于事:

    仔细检查发现异常:

    org.springframework.expression.AccessException: Could not resolve bean reference against BeanFactory
    

    有问题的参考是我在实用程序中使用@SpyBean 的方法:

        <int:transformer
            expression="@utilities.asMap('licence_id', headers[licenceId], 'message', 'Delivered: ' + headers[confirmedMessage], 'secured_session_id', headers[visitorSession].getSecureSessionId())" />
    

    它不是一个单独的 ApplicationContext,而是 SpEL 不会接受 spy bean,因为引用已更改或类似。

    所以,我不理会这些实用程序,并在其内部改装了另一个 bean 以生成数字,并在其上使用了 SpyBean。现在 Spring Integration/SpEL 再次感到高兴,因为它使用的实用程序 bean 是正确的,并且模拟发生在该 bean 内部并且对 SpEL 是透明的。

    @Component
    public class RandomSupplier implements Supplier<Double> {
    
        @Override
        public Double get() {
            return Math.random();
        }
    }
    
    public class FullIntegrationTest {
    
        @Autowired
        private MockMvc mvc;
    
        @SpyBean
        private RandomSupplier randomSupplier;
    
        @Autowired // This is only necessary for the toy test below
        private Utilities utilities;
    
        @BeforeEach
        public void setupAfterInit() {
    
            Mockito.when(randomSupplier.get()).thenReturn(0.5);
        }
    
        @Test
        public void t0() throws IOException {
          System.out.println(utilities.getRandom());
        }
    ...
    

    现在 Spring Integration/SpEL 再次感到高兴,因为它工作的实用程序 bean 是正确的,并且模拟发生在该 bean 内部。

    三课:不要在 Spring Integration Flow 中窥探 SpEL 中直接引用的 bean;阅读日志;你永远不会有足够的间接性:)

    【讨论】:

      猜你喜欢
      • 2017-04-10
      • 2020-03-09
      • 2015-09-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-04-21
      相关资源
      最近更新 更多