【问题标题】:JUnit Mock outside rest call via restTemplateJUnit 通过restTemplate 模拟外部休息呼叫
【发布时间】:2020-08-22 07:06:50
【问题描述】:

我正在尝试编写一个集成测试,其中在模拟使用 JUnit 调用外部服务器的其余调用时遇到问题。 我在服务中添加了 @Mock@InjectMock

服务看起来像这样。

@Service
public class BoundaryDeltaService {
    private BoundaryDelta getBoundaryDeltaUsingApp() {
       List<BoundaryValueInfo> infoFromSource = Arrays.asList(serviceAdapter.readInfoFromApiUsingApp(boundarySet, app, loginUserInfo));
      return  getBoundaryDeltacompareCurrentBoundaryValuesWithSource(boundarySet, infoFromSource );
    }
}

有另一个服务有 restTemplate 调用

@Service
public class ServiceAdapter {
    public BoundaryValueInfo[] readInfoFromApiUsingApp(){
        String loginToken = systemUserLoginService.getSystemUserTokenManual(app, loginUserInfo);
        restTemplate = createRestTemplate(boundarySet);
        HttpHeaders headers = new HttpHeaders() {{
          String authHeader = "Bearer " + loginToken;
          set( "Authorization", authHeader );
        }};

        HttpEntity<String> request = new HttpEntity<String>(headers);
        ResponseEntity<BoundaryValueInfo[]> answerFromApi = restTemplate.exchange(boundarySet.getApiUri(), HttpMethod.GET, request,  BoundaryValueInfo[].class);
        return getResponseFromApi(answerFromApi);
    }
}

这是测试场景

@RunWith(SpringJUnit4ClassRunner.class)
@ActiveProfiles({"aws", "local"})
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = FlywayConfig.class)
@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS)
public class BoundaryValueDeltaControllerTest {

    private static final String API_V1 = "/api/v1/";

    @Autowired
    TestRestTemplate testRestTemplate;

    @Autowired
    private BoundaryDeltaService boundaryDeltaService;

    @Autowired
    private DomainBuilder domainBuilder;

    @Autowired
    private AppBuilder appBuilder;

    @Autowired
    private AppAdminBuilder appAdminBuilder;

    @Autowired
    private BoundaryValueBuilder boundaryValueBuilder;

    @Autowired
    private BoundarySetBuilder boundarySetBuilder;

    @MockBean
    private LoginUserProvider loginUserProvider;
    @MockBean
    private LoginTokenService loginTokenService;
    @InjectMocks
    private BoundaryServiceAdapter boundaryServiceAdapter;

    @Mock
    RestTemplate restTemplate;

    @LocalServerPort
    private int port;

    Domain domain;
    App app;
    BoundarySet boundarySet;
    BoundaryValue boundaryValue;
    LoginUserInfo loggedInUser;

    @Before
    public void setUp() {
        clear();
        domain = domainBuilder.persist();
        app = appBuilder.persist(domain);
        boundarySet =  boundarySetBuilder.persist(domain);
        boundaryValue = boundaryValueBuilder.persist(boundarySet);
    }

    @After
    public void tearDown() {
        clear();
    }

    @BeforeClass
    public static void setupTestEnv() {
        // https://github.com/localstack/localstack/issues/592
    }

    @Test
    public void updateBoundaryValuesFromApi() {
        aLoggedInUser(domain.getAuthor().getUsername());
        appAdminBuilder.persist(app, domain.getAuthor());
        ResponseEntity<BoundaryValueInfo[]> answerFromApi = getBoundaryValueInfos();

        HttpHeaders headers = new HttpHeaders() {{
            String authHeader = "Bearer 1234";
            set( "Authorization", authHeader );
        }};

        HttpEntity<String> request = new HttpEntity<>(headers);
        //when(restTemplate.exchange(boundarySet.getApiUri(), HttpMethod.GET, request,  BoundaryValueInfo[].class)).thenReturn(answerFromApi);

        when(restTemplate.exchange(ArgumentMatchers.anyString(),
                ArgumentMatchers.any(HttpMethod.class),
                ArgumentMatchers.any(),
                ArgumentMatchers.<Class<BoundaryValueInfo[]>>any())
        ).thenReturn(answerFromApi);

        String url = url(API_V1 + "domains/" + domain.getName() + "/boundarysets/" + boundarySet.getBoundarySetName() + "/app/" + app.getName()+ "/updatefromapi/");
        ResponseEntity<String> response = testRestTemplate.exchange(url,HttpMethod.GET, null, String.class);

        assertEquals(HttpStatus.OK, response.getStatusCode());
    }
}

我正在使用 api 调用控制器,并从那里进入上述服务,该服务具有休息调用但无法模拟实际调用。 有人可以在这里指导我吗?

【问题讨论】:

  • restTemplate in ServiceAdapter 不是自动连线的。它是使用createRestTemplate(boundarySet); 创建的。如果您的实现是这样的,则不能直接注入模拟。您可以重构您的代码并将createRestTemplate(boundarySet); 移动到单独的类并在junit 中模拟该类,
  • @Smile 就像,我不能模拟 ServiceAdapter 因为它是集成测试,我还必须自动装配 RestTemplate。我的createRestTemplate(boundarySet) 什么都不做,但看起来像这样。在完全单独的呼叫中移动它是否相关? private RestTemplate createRestTemplate(BoundarySet boundarySet) { return new RestTemplate(); }
  • createRestTemplate() 移动到像'RestTemplateFactory' 这样的单独类,然后在你的junit 中模拟RestTemplateFactory,而ServiceAdapterRestTemplateFactory 作为@Autowired
  • @Smile 好的,但问题是当你调用 restTemplate.exchange() 时。我如何设法在 when().thenReurn() 中模拟它?
  • 你可以模拟restTemplateFactory.createRestTemplate()返回模拟的rest模板对象

标签: java spring junit4 spring-junit


【解决方案1】:

您没有将 TestRestTemplate 用于其预期用途。

TestRestTemplate 不是 RestTemplate 的扩展,而是一种简化集成测试并促进测试期间身份验证的替代方案。它有助于自定义 Apache HTTP 客户端,也可以用作 RestTemplate 的包装器

解决方案:

方法1: 1) 使用 RestTemplate 对象直接调用交换方法。

    ResponseEntity<String> response = restTemplate .exchange(url,HttpMethod.GET, null, String.class);

2) 模拟 testRestTemplate 并在调用此对象的交换方法时进行模拟。

    when(testRestTemplate.exchange(ArgumentMatchers.anyString(),
            ArgumentMatchers.any(HttpMethod.class),
            ArgumentMatchers.any(),
            ArgumentMatchers.<Class<BoundaryValueInfo[]>>any())
    ).thenReturn(answerFromApi);

【讨论】:

  • 我将如何直接调用它?这是一个集成测试,我们不能像这样中断流程。单独调用它不会落入集成测试中。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-07
  • 1970-01-01
  • 1970-01-01
  • 2011-10-31
  • 1970-01-01
相关资源
最近更新 更多