@RefreshScope 似乎忽略了 Mockito 的模拟
@RefreshScope seems to ignore Mockito's mocks
我正在使用 Spring Boot 和 Spring Cloud Config 服务实现一项服务以提供配置值。在我的服务中,我有几个配置值需要在远程 Git 存储库中的值更改时刷新,我使用 @RefreshScope
来启用该功能。
当我尝试在该服务中为 RestTemplate
注入模拟时,问题就出现了,它似乎忽略了它并改用自动装配的实例。如果我注释掉注释,它似乎工作正常。
这是服务的代码:
@Service
@RefreshScope
public class MyServiceImpl implements MyService {
private static final Logger LOG = Logger.getLogger(MyServiceImpl.class);
@Autowired
public RestTemplate restTemplate;
@Value("${opts.default}")
private String default;
@Value("${opts.address}")
private String address;
@Value("${opts.separator}")
private String separator;
...
}
测试源代码:
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class)
public class ServiceTest {
@Mock
private RestTemplate restTemplate;
@Autowired
@InjectMocks
private MyServiceImpl service;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
public void testMethod() throws Exception {
when(restTemplate.postForObject(anyString(), any(), eq(ServiceResponse.class), anyMap())).thenReturn(getSuccessfulResponse());
ServiceResponse response = service.doYourStuff();
Assert.assertNotNull(response);
Assert.assertTrue(response.isSuccessful());
}
...
}
添加 @RefreshScope
时,bean 成为代理而不是实际的原始实现。目前 RestTemplate
是在代理上设置的,而不是在底层实例上。 (如果您进行调试,您会发现您的 MyServiceImpl
实际上更像是 MyServiceImpl$SpringCgLib#353234
的一个实例)。
要修复,您需要使用 ReflectionTestUtils
和 AopTestUtils
手动设置依赖关系。后者是获取实际代理。
删除 @InjectMocks
注释并在模拟初始化后将以下内容添加到您的 setup
方法中:
Object actualTarget = AopTestUtils.getUltimateTargetObject(service);
ReflectionTestUtils.setfield(actualTarget, "restTemplate", restTemplate);
对于 4.2 之前的版本,以下可能会起作用
Object actualTarget = (service instanceof Advised) ? ((Advised) service).getTargetSource().getTarget() : service;
问题是 Mockito 没有检测到代理,只是设置了字段。 ReflectionTestUtils
也没有检测到代理,因此手动解包。事实上,我之前曾多次踏入这个陷阱,这导致我今天早上创建 SPR-14050 并将其嵌入 ReflectionTestUtils
中以减轻痛苦。
我正在使用 Spring Boot 和 Spring Cloud Config 服务实现一项服务以提供配置值。在我的服务中,我有几个配置值需要在远程 Git 存储库中的值更改时刷新,我使用 @RefreshScope
来启用该功能。
当我尝试在该服务中为 RestTemplate
注入模拟时,问题就出现了,它似乎忽略了它并改用自动装配的实例。如果我注释掉注释,它似乎工作正常。
这是服务的代码:
@Service
@RefreshScope
public class MyServiceImpl implements MyService {
private static final Logger LOG = Logger.getLogger(MyServiceImpl.class);
@Autowired
public RestTemplate restTemplate;
@Value("${opts.default}")
private String default;
@Value("${opts.address}")
private String address;
@Value("${opts.separator}")
private String separator;
...
}
测试源代码:
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class)
public class ServiceTest {
@Mock
private RestTemplate restTemplate;
@Autowired
@InjectMocks
private MyServiceImpl service;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
public void testMethod() throws Exception {
when(restTemplate.postForObject(anyString(), any(), eq(ServiceResponse.class), anyMap())).thenReturn(getSuccessfulResponse());
ServiceResponse response = service.doYourStuff();
Assert.assertNotNull(response);
Assert.assertTrue(response.isSuccessful());
}
...
}
添加 @RefreshScope
时,bean 成为代理而不是实际的原始实现。目前 RestTemplate
是在代理上设置的,而不是在底层实例上。 (如果您进行调试,您会发现您的 MyServiceImpl
实际上更像是 MyServiceImpl$SpringCgLib#353234
的一个实例)。
要修复,您需要使用 ReflectionTestUtils
和 AopTestUtils
手动设置依赖关系。后者是获取实际代理。
删除 @InjectMocks
注释并在模拟初始化后将以下内容添加到您的 setup
方法中:
Object actualTarget = AopTestUtils.getUltimateTargetObject(service);
ReflectionTestUtils.setfield(actualTarget, "restTemplate", restTemplate);
对于 4.2 之前的版本,以下可能会起作用
Object actualTarget = (service instanceof Advised) ? ((Advised) service).getTargetSource().getTarget() : service;
问题是 Mockito 没有检测到代理,只是设置了字段。 ReflectionTestUtils
也没有检测到代理,因此手动解包。事实上,我之前曾多次踏入这个陷阱,这导致我今天早上创建 SPR-14050 并将其嵌入 ReflectionTestUtils
中以减轻痛苦。