【问题标题】:Unable to Mock RestTemplate.exchange无法模拟 RestTemplate.exchange
【发布时间】:2019-05-15 06:11:31
【问题描述】:

作为 TDD 的一部分,我希望能够测试我的 SpringBoot REST 应用程序的每个部分。但是我无法模拟外部呼叫。

应用结构
1.很少有内部调用外部休息端点的休息端点。
2. 对外部端点的所有调用都通过本地 http 客户端进行编排,该客户端使用 RestTemplate 作为 httpClient。

@RunWith(SpringRunner.class)  

@SpringBootTest(webEnvironment = WebEnvironment.MOCK, classes = TestDrivenDevelopmentWithJavaApplication.class)  

public class TestDrivenDevelopmentWithJavaApplicationTests {
@Autowired
private WebApplicationContext webApplicationContext;
private MockMvc mockMvc;
@MockBean
private RestTemplate client;

@Before
public void setup() throws Exception {
    mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
    Structure1Root category = new Structure1Root();
    Category cat = new Category();
    cat.setCategoryName("Test1");
    cat.setDescription("Test");
    category.setD(cat);

    Mockito.when(client.exchange(
            ArgumentMatchers.eq("https://services.odata.org/V2/Northwind/Northwind.svc/Products(1)?$format=json"),
            ArgumentMatchers.eq(HttpMethod.GET), ArgumentMatchers.eq(null),
            ArgumentMatchers.eq(Structure1Root.class)))
            .thenReturn(new ResponseEntity<Structure1Root>(category, HttpStatus.OK));
}

@Test
public void testendpoint1() throws Exception {
    mockMvc.perform(MockMvcRequestBuilders.get("/api/endpoint1?token=1").contentType(MediaType.APPLICATION_JSON))
            .andExpect(content().string(org.hamcrest.Matchers.containsString("Test1")));

}

}

即使我在 client.exchange(RestTemplate.exchange) 上设置了模拟,我看到 client.exchange 返回的响应为空,而不是 thenReturn 中指定的响应

控制器代码

@RestController
@RequestMapping(path = Endpoint.base)
public class Endpoint {
    public static final String base = "/api";
    @Autowired
    MyHttpClient<Structure2Root> client;
    @Autowired
    MyHttpClient<Structure1Root> Cclient;

    @GetMapping(path = "/endpoint1")
    public ResponseEntity<Structure2Root> callEndpt1(@RequestParam String token) {
        Response<Structure2Root> resp = client
                .execute("https://services.odata.org/V2/Northwind/Northwind.svc/Products(1)?$format=json", Structure2Root.class);
        return new ResponseEntity<Structure2Root>(resp.getResponse(), HttpStatus.OK);
    }

    @GetMapping(path = "/endpoint2")
    public ResponseEntity<Structure1Root> callEndpt2(@RequestParam String token) {
        Response<Structure1Root> resp = Cclient.execute(
                "https://services.odata.org/V2/Northwind/Northwind.svc/Categories(1)?$format=json", Structure1Root.class);
        return new ResponseEntity<Structure1Root>(resp.getResponse(),HttpStatus.OK);
    }

}

最后,本地http客户端代码

@Service
public class MyHttpClient<O> {
    @Autowired
    RestTemplate client;

    public MyHttpClient() {
        // TODO Auto-generated constructor stub
    }

    public Response<O> execute(String url, Class<O> generic) {
        ResponseEntity<O> resp = client.exchange(url, HttpMethod.GET, null, generic);
        return new Response<O>(resp.getStatusCode(), resp.getBody());
    }

}

这个client.execute 是我打算在第一个代码块中拦截的内容

但是似乎从来没有工作并且总是返回一个空值。 Git Repo

问候,
维拉

【问题讨论】:

  • 返回 null 是模拟的默认行为。因此,您用于设置模拟的 expactations/predicates 与实际值不匹配。查看您的方法,它需要 Structure2Root 而不是 Structure1Root 用于在您的模拟上设置您的预期。
  • @M.Deinum:感谢您的帮助。肯定解决了。一个重要的经验教训是模拟的默认行为。我的误解是我认为默认行为是调用 rest 端点并仅模拟配置了 mockito 的请求。

标签: spring-boot mockito tdd resttemplate mockmvc


【解决方案1】:

您在模拟时使用了错误的对象。您应该使用 Structure2Root 而不是 Structure1Root

正确的测试类在下面,它工作得很好。

package com.demo.samples.tdd;

import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import com.demo.samples.tdd.responses.Product;
import com.demo.samples.tdd.responses.Structure2Root;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentMatchers;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.context.WebApplicationContext;

import com.demo.samples.tdd.responses.Category;
import com.demo.samples.tdd.responses.Structure1Root;

@RunWith(SpringRunner.class)

@SpringBootTest(webEnvironment = WebEnvironment.MOCK, classes = TestDrivenDevelopmentWithJavaApplication.class)
public class TestDrivenDevelopmentWithJavaApplicationTests {
    @Autowired
    private WebApplicationContext webApplicationContext;
    private MockMvc mockMvc;
    @MockBean
    private RestTemplate client;

    @Before
    public void setup() throws Exception {
        mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
//      Structure1Root category = new Structure1Root();
//      Category cat = new Category();
//      cat.setCategoryName("Test1");
//      cat.setDescription("Test");
//      category.setD(cat);
//
//      Mockito.when(client.exchange(
//              ArgumentMatchers.eq("https://services.odata.org/V2/Northwind/Northwind.svc/Products(1)?$format=json"),
//              ArgumentMatchers.eq(HttpMethod.GET), ArgumentMatchers.eq(null),
//              ArgumentMatchers.eq(Structure1Root.class)))
//              .thenReturn(new ResponseEntity<Structure1Root>(category, HttpStatus.OK));


        Structure2Root category2 = new Structure2Root();
        Product product = new Product();
        product.setProductName("Test1");
        product.setUnitPrice("1");
        category2.setD(product);

        Mockito.when(client.exchange(
                ArgumentMatchers.eq("https://services.odata.org/V2/Northwind/Northwind.svc/Products(1)?$format=json"),
                ArgumentMatchers.eq(HttpMethod.GET), ArgumentMatchers.eq(null),
                ArgumentMatchers.eq(Structure2Root.class)))
                .thenReturn(new ResponseEntity<Structure2Root>(category2, HttpStatus.OK));


    }

    @Test
    public void testendpoint1() throws Exception {
        mockMvc.perform(MockMvcRequestBuilders.get("/api/endpoint1?token=1").contentType(MediaType.APPLICATION_JSON))
                .andExpect(content().string(org.hamcrest.Matchers.containsString("Test1")));

    }

}

【讨论】:

    猜你喜欢
    • 2020-09-13
    • 2019-06-22
    • 2019-12-13
    • 1970-01-01
    • 2018-11-17
    • 2021-09-14
    • 1970-01-01
    • 2017-07-14
    • 1970-01-01
    相关资源
    最近更新 更多