精华内容
下载资源
问答
  • Mock 测试

    2020-10-31 19:10:44
    mock 测试就是在测试过程中,对于某些不容易构造或者不容易获取的对象,用一个虚拟的对象来创建以便测试测试方法。 好处 团队并行工作 团队间不需互相等待对方进度,只需约定好相互之间的数据规范(接口文档)...

    Mock 基本概念介绍

    mock 测试就是在测试过程中,对于某些不容易构造或者不容易获取的对象,用一个虚拟的对象来创建以便测试的测试方法。

    好处

    1. 团队并行工作
      团队间不需互相等待对方进度,只需约定好相互之间的数据规范(接口文档),即可使用 mock 构建出可用接口,然后尽快进行开发和自测,提前发现缺陷
    2. 测试驱动开发 TDD (Test-Driven Development)
      单元测试是 TDD 实现的基石,而 TDD 经常会碰到协同模块尚未开发完成的情况,但有了 mock,当接口定义好后,测试人员就可以创建一个 Mock,把接口添加到自动化测试环境,提前创建测试。
    3. 测试覆盖率
      若一个接口在不同的状态下要返回不同的值,常见做法是复现这种状态然后再去请求接口,而这种方法很可能因操作时机或方式不当导致失败,甚至污染后端存储如数据库等, 但用 mock 则不用担心
    4. 隔离系统
      使用某些接口时,为避免系统数据库被污染,可以将接口调整为 Mock 模式,以保证数据库纯净。
    5. 方便演示

    Mock 框架介绍

    因为项目主要基于 Java 开发, 因此下面主要介绍 Java 相关的 mock 框架, 其他语言思想类似

    moco

    moco 框架在开发 Mock 服务的时候提供了一种不需任何编程语言的方式, 可以通过撰写它约束的 json 建立服务, 并通过命令独立启动对应的服务, 这可以快速开发和启动运行所需的 Mock 服务. 除此之外, 也可以编写服务代码来进行测试. 下面进行简单举例:

    1. 使用 json 配置文件启动 mock 服务

     

    # foo.json
    [
      {
        "response" :
          {
            "text" : "Hello, Moco"
          }
      }
    ]
    

     

    java -jar moco-runner-1.1.0-standalone.jar  http -p 12306 -c foo.json
    

    这时访问 http://localhost:12306/ 将会返回 Hello, Moco

    1. 在项目中使用 moco Java API
      除了使用 json 配置文件作为独立服务启动外, 还可以使用 Java API 来启动 mock 服务, 下面是代码片段:

     

    @RunWith(SpringRunner.class)
    @SpringBootTest(classes = MockServletContext.class)
    public class MockAPITest {
        @Test
        public void should_response_as_expected() throws Exception {
            HttpServer server = httpServer(12307);
            server.response("foo");
            running(server, new Runnable() {
                @Override
                public void run() throws IOException {
                    CloseableHttpResponse response = HttpClients.createDefault().execute(new HttpGet("http://localhost:12307"));
                    String content = EntityUtils.toString(response.getEntity(), "UTF-8");
                    assertThat(content, is("foo"));
                }
            });
        }
    }
    

    moco 还支持 HTTPS 和 Socket, 支持与 JUnit 集成等, 详细内容见文档使用说明

    SpringMVC 单元测试 - MockMvc

    MockMvc 实现了对 Http 请求的模拟,可以方便对 Controller 进行测试,测试速度快、不依赖网络环境,且提供了验证的工具。下面是具体示例:

    • HelloController

     

    //HelloController
    @RestController
    public class HelloController {
        @RequestMapping("/hello")
        public String index() {
            return "Hello World";
        }
    }
    
    • UserController

     

    //UserController
    @Slf4j
    @RestController
    @RequestMapping(value = "/users")     // 通过这里配置使下面的映射都在/users下
    public class UserController {
        // 创建线程安全的Map
        static Map<Long, User> users = Collections.synchronizedMap(new HashMap<Long, User>());
    
        @RequestMapping(value = "/", method = RequestMethod.GET)
        public List<User> getUserList() {
            // 处理"/users/"的GET请求,用来获取用户列表
            // 还可以通过@RequestParam从页面中传递参数来进行查询条件或者翻页信息的传递
            List<User> r = new ArrayList<User>(users.values());
            return r;
        }
    
        @RequestMapping(value = "/", method = RequestMethod.POST)
        public String postUser(@ModelAttribute User user) {
            // 处理"/users/"的POST请求,用来创建User
            // 除了@ModelAttribute绑定参数之外,还可以通过@RequestParam从页面中传递参数
            users.put(user.getId(), user);
            return "success";
        }
    
        @RequestMapping(value = "/{id}", method = RequestMethod.GET)
        public User getUser(@PathVariable Long id) {
            // 处理"/users/{id}"的GET请求,用来获取url中id值的User信息
            // url中的id可通过@PathVariable绑定到函数的参数中
            return users.get(id);
        }
    
        @RequestMapping(value = "/{id}", method = RequestMethod.PUT)
        public String putUser(@PathVariable Long id, @ModelAttribute User user) {
            // 处理"/users/{id}"的PUT请求,用来更新User信息
            User u = users.get(id);
            u.setName(user.getName());
            u.setAge(user.getAge());
            users.put(id, u);
            return "success";
        }
    
        @RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
        public String deleteUser(@PathVariable Long id) {
            // 处理"/users/{id}"的DELETE请求,用来删除User
            users.remove(id);
            return "success";
        }
        // 测试 
        @RequestMapping(value = "/postByJson", method = RequestMethod.POST)
        public String postByJson(@RequestBody User user, String method) {
            log.info("user: {};   method: {}", user, method);
            return "success";
        }
    }
    
    
    • 单元测试类 HttpMockTest

     

    public class HttpMockTest {
    
        private MockMvc mvc;
        private final static ObjectMapper objectMapper = new ObjectMapper();
    
        @Before
        public void setUp() throws Exception {
            mvc = MockMvcBuilders.standaloneSetup(
                    new HelloController(),
                    new UserController()).build();
        }
    
        @Test
        public void getHello() throws Exception {
            mvc.perform(MockMvcRequestBuilders.get("/hello").accept(MediaType.APPLICATION_JSON))
                    .andExpect(status().isOk())
                    .andExpect(content().string(equalTo("Hello World")));
        }
    
        @Test
        public void testUserController() throws Exception {
            // 测试UserController
            RequestBuilder request = null;
    
            // 1、get查一下user列表,应该为空
            request = get("/users/");
            mvc.perform(request)
                    .andExpect(status().isOk())
                    .andExpect(content().string(equalTo("[]")));
    
            // 2、post提交一个user
            request = post("/users/")
                    .param("id", "1")
                    .param("name", "测试大师")
                    .param("age", "20");
            mvc.perform(request)
                    .andDo(MockMvcResultHandlers.print())
                    .andExpect(content().string(equalTo("success")));
    
            // 3、get获取user列表,应该有刚才插入的数据
            request = get("/users/");
            mvc.perform(request)
                    .andExpect(status().isOk())
                    .andExpect(content().string(equalTo("[{\"id\":1,\"name\":\"测试大师\",\"age\":20}]")));
    
            // 4、put修改id为1的user
            request = put("/users/1")
                    .param("name", "测试终极大师")
                    .param("age", "30");
            mvc.perform(request)
                    .andExpect(content().string(equalTo("success")));
    
            // 5、get一个id为1的user
            request = get("/users/1");
            mvc.perform(request)
                    .andExpect(content().string(equalTo("{\"id\":1,\"name\":\"测试终极大师\",\"age\":30}")));
    
            // 6、del删除id为1的user
            request = delete("/users/1");
            mvc.perform(request)
                    .andExpect(content().string(equalTo("success")));
    
            // 7、get查一下user列表,应该为空
            request = get("/users/");
            mvc.perform(request)
                    .andExpect(status().isOk())
                    .andExpect(content().string(equalTo("[]")));
    
            // 8、json作为参数
            request = post("/users/postByJson")
                    .param("method", "postByJson")
                    .content(objectMapper.writeValueAsString(new User(1L, "USER", 23)))
                    .contentType(MediaType.APPLICATION_JSON);
            mvc.perform(request).andExpect(status().is(200))
                    .andExpect(content().string("success"));
        }
    }
    

    Mockito & PowerMock

    Mockito 是 GitHub 上使用非常广泛的 Java Mock 框架, star 数 11k, 在包括 openstack4jkubernetes-client/java 等都有用到. Mockito 与 JUnit 结合使用, 能隔离外部依赖以便对自己的业务逻辑代码进行单元测试在编写单元测试需要调用某一个接口时,可以模拟一个假方法,并任意指定方法的返回值。Mockito 的工作原理是通过创建依赖对象的 proxy,所有的调用先经过 proxy 对象,proxy 对象拦截了所有的请求再根据预设的返回值进行处理。但缺点是 Mockito 2 版本对静态方法、final 方法、private 方法和构造函数的功能支持并不完善, 因此 PowerMock 则在 Mockito 原有的基础上做了扩展,通过修改类字节码并使用自定义 ClassLoader 加载运行的方式来实现 mock 静态方法、final 方法、private 方法和构造函数等功能。

    Mockito & PowerMock 一般测试步骤

    1. mock: 模拟对象

    用 mock()/@Mock 或 spy()/@Spy 创建模拟对象, 两者创建出来的模拟对象区别是: 使用 mock 生成的对象,所有方法都是被 mock 的,除非某个方法被 stub 了,否则返回值都是默认值; 使用 spy 生产的 spy 对象,所有方法都是调用的 spy 对象的真实方法,直到某个方法被 stub 后

    2. stub: 定义桩函数

    可以通过 when()/given()/thenReturn()/doReturn()/thenAnswer() 等来定义 mock 对象如何执行, 如果提供的接口不符合需求, 还可以通过实现 Answer 接口来自定义实现

    3. run: 执行调用

    执行实际方法的调用,此时被 mock 的对象将返回自定义的桩函数的返回值

    4. verify: 可选, 对调用进行验证, 如是否被调用, 调用次数等

    这一步可以对 mock 对象的方法是否被调用以及被调用次数进行验证,同时还可以对参数捕获进行参数校验

    下面以操作 Redis 和 RabbitMQ 来进行简单举例。

    Redis

     

    // redis 操作类
    class RedisDemo {
    
        private Jedis jedis;
    
        public void setUp() {
            jedis = new Jedis("127.0.0.1", 6379);
            jedis.connect();
        }
    
        public boolean isAdmin(String user) {
            String ret = jedis.get("name");
            if (user.equals(ret)) {
                return true;
            }
            return false;
        }
    
        public void set(String key, String val) {
            jedis.set(key, val);
        }
    
        public String get(String key) {
            String s = jedis.get(key);
            return s;
        }
    
        void out(){
            System.out.println("ss");
        }
    }
    
    // 单元测试类
    @RunWith(PowerMockRunner.class) //让测试运行于PowerMock环境
    public class RedisMockitoTest {
    
        @Mock //此注解会自动创建1个mock对象并注入到@InjectMocks对象中
        private Jedis jedis;
    
        @InjectMocks
        private RedisDemo demo;
    
        @Mock
        StringOperator stringOperator;
    
        //第1种方式
        @Test
        public void redisTest1() throws Exception {
            Mockito.when(jedis.get("name")).thenReturn("admin");
            boolean admin = demo.isAdmin("admin");
            assertTrue(admin);
        }
    
        //第2种方式
        @Test
        public void redisTest2() {
            RedisDemo demo = mock(RedisDemo.class);
            ReflectionTestUtils.setField(demo, "jedis", jedis);
            when(demo.isAdmin("admin")).thenReturn(true);
            boolean admin = demo.isAdmin("admin");
            assertTrue(admin);
        }
    
        //第3种方式
        @Test
        public void redisTest3() {
            RedisDemo demo = mock(RedisDemo.class);
            doReturn(true).when(demo).isAdmin("admin");
            System.out.println(demo.isAdmin("admin"));
        }
    }
    

    RabbitMQ

     

    @Component
    public class DirectReceiver {
        @Autowired
        RabbitTemplate rabbitTemplate;
    
        public Object getMsg() {
            return rabbitTemplate.receiveAndConvert("queue_demo");
        }
    }
    
    @RunWith(SpringJUnit4ClassRunner.class)
    @SpringBootTest(classes = Main.class)
    public class RecvMessage {
        @Spy
        RabbitTemplate rabbitTemplate;
    
        @InjectMocks
        @Autowired
        DirectReceiver receiver;
    
        @Test
        public void recvTest() {
            doReturn("Mock answer").when(rabbitTemplate).receiveAndConvert("queue_demo");
            System.out.println(rabbitTemplate.receiveAndConvert("queue_demo"));
        }
    }
    

    更多示例

     

    public class Node {
        private int num;
        private String name;
    
        public static Node getStaticNode() {
            return new Node(1, "static node");
        }
    
        public Node() {
        }
    
        public Node(String name) {
            this.name = name;
        }
    
        public Node(int num) {
            this.num = num;
        }
    
        public Node(int num, String name) {
            this.num = num;
            this.name = name;
        }
    }
    
    public class LocalServiceImpl implements ILocalService {
    
        @Autowired
        private IRemoteService remoteService;
    
        @Override
        public Node getLocalNode(int num, String name) {
            return new Node(num, name);
        }
    
        @Override
        public Node getRemoteNode(int num) {
            return remoteService.getRemoteNode(num);
        }
    
        @Override
        public Node getRemoteNode(String name) throws MockException {
            try {
                return remoteService.getRemoteNode(name);
            } catch (IllegalArgumentException e) {
                throw e;
            }
        }
    
        @Override
        public void remoteDoSomething() {
            remoteService.doSometing();
        }
    
    }
    
    public class RemoteServiceImpl implements IRemoteService {
    
        @Override
        public Node getRemoteNode(int num) {
            return new Node(num, "Node from remote service");
        }
    
        @Override
        public final Node getFinalNode() {
            return new Node(1, "final node");
        }
    
        @Override
        public Node getRemoteNode(String name) throws MockException {
            if (StringUtils.isEmpty(name)) {
                throw new MockException("name不能为空", name);
            }
            return new Node(name);
        }
    
        @Override
        public void doSometing() {
            System.out.println("remote service do something!");
        }
    
        @Override
        public Node getPrivateNode() {
            return privateMethod();
        }
    
        private Node privateMethod() {
            return new Node(1, "private node");
        }
    
        @Override
        public Node getSystemPropertyNode() {
            return new Node(System.getProperty("abc"));
        }
    }
    
    // 单元测试类
    @RunWith(MockitoJUnitRunner.class) //让测试运行于Mockito环境
    public class LocalServiceImplMockTest {
    
        @InjectMocks //此注解表示这个对象需要被注入mock对象
        private LocalServiceImpl localService;
        @Mock //此注解会自动创建1个mock对象并注入到@InjectMocks对象中
        private RemoteServiceImpl remoteService;
        @Captor
        private ArgumentCaptor<String> localCaptor;
    
        //如果不使用上述注解,可以使用@Before方法来手动进行mock对象的创建和注入,但会多几行代码
        /*@Before
        public void setUp() throws Exception {
            localService = new LocalServiceImpl();
            remoteService = mock(RemoteServiceImpl.class);
            Whitebox.setInternalState(localService, "remoteService", remoteService);
        }*/
    
        /**
         * any系列方法指定多参数情况
         */
        @Test
        public void testAny() {
            Node target = new Node(1, "target");
            when(remoteService.getRemoteNode(anyInt())).thenReturn(target); //静态导入Mockito.when和ArgumentMatchers.anyInt后可以简化代码提升可读性
    
            Node result = localService.getRemoteNode(20); //上面指定了调用remoteService.getRemoteNode(int)时,不管传入什么参数都会返回target对象
            assertEquals(target, result);   //可以断言我们得到的返回值其实就是target对象
            assertEquals(1, result.getNum());   //具体属性和我们指定的返回值相同
            assertEquals("target", result.getName());   //具体属性和我们指定的返回值相同
        }
    
        /**
         * 指定mock多次调用返回值
         */
        @Test
        public void testMultipleReturn() {
            Node target1 = new Node(1, "target");
            Node target2 = new Node(1, "target");
            Node target3 = new Node(1, "target");
            when(remoteService.getRemoteNode(anyInt())).thenReturn(target1).thenReturn(target2).thenReturn(target3);
            //第一次调用返回target1、第二次返回target2、第三次返回target3
    
            Node result1 = localService.getRemoteNode(1); //第1次调用
            assertEquals(target1, result1);
            Node result2 = localService.getRemoteNode(2); //第2次调用
            assertEquals(target2, result2);
            Node result3 = localService.getRemoteNode(3); //第3次调用
            assertEquals(target3, result3);
        }
    
        /**
         * 指定mock对象已声明异常抛出的方法抛出受检查异常
         */
        @Test
        public void testCheckedException() {
            try {
                Node target = new Node(1, "target");
                when(remoteService.getRemoteNode("name")).thenReturn(target).thenThrow(new MockException("message", "exception")); //第一次调用正常返回,第二次则抛出一个Exception
    
                Node result1 = localService.getRemoteNode("name");
                assertEquals(target, result1); //第一次调用正常返回
    
                Node result2 = localService.getRemoteNode("name"); //第二次调用不会正常返回,会抛出异常
                assertEquals(target, result2);
            } catch (MockException e) {
                assertEquals("exception", e.getName()); //验证是否返回指定异常内容
                assertEquals("message", e.getMessage()); //验证是否返回指定异常内容
            }
        }
    
        /**
         * 校验mock对象和方法的调用情况
         */
        public void testVerify() {
            Node target = new Node(1, "target");
            when(remoteService.getRemoteNode(anyInt())).thenReturn(target);
    
            verify(remoteService, Mockito.never()).getRemoteNode(1); //mock方法未调用过
    
            localService.getRemoteNode(1);
            verify(remoteService, times(1)).getRemoteNode(anyInt()); //目前mock方法调用过1次
    
            localService.getRemoteNode(2);
            verify(remoteService, times(2)).getRemoteNode(anyInt()); //目前mock方法调用过2次
            verify(remoteService, times(1)).getRemoteNode(2); //目前mock方法参数为2只调用过1次
        }
    
        /**
         * mock对象调用真实方法
         */
        @Test
        public void testCallRealMethod() {
            when(remoteService.getRemoteNode(anyInt())).thenCallRealMethod(); //设置调用真实方法
            Node result = localService.getRemoteNode(1);
    
            assertEquals(1, result.getNum());
            assertEquals("Node from remote service", result.getName());
        }
    
        /**
         * 利用ArgumentCaptor捕获方法参数进行mock方法参数校验
         */
        @Test
        public void testCaptor() throws Exception {
            Node target = new Node(1, "target");
            when(remoteService.getRemoteNode(anyString())).thenReturn(target);
    
            localService.getRemoteNode("name1");
            localService.getRemoteNode("name2");
            verify(remoteService, atLeastOnce()).getRemoteNode(localCaptor.capture()); //设置captor
    
            assertEquals("name2", localCaptor.getValue()); //获取最后一次调用的参数
            List<String> list = localCaptor.getAllValues(); //按顺序获取所有传入的参数
            assertEquals("name1", list.get(0));
            assertEquals("name2", list.get(1));
        }
    
        /**
         * 校验mock对象0调用和未被验证的调用
         */
        @Test(expected = NoInteractionsWanted.class)
        public void testInteraction() {
    
            verifyZeroInteractions(remoteService); //目前还未被调用过,执行不报错
    
            Node target = new Node(1, "target");
            when(remoteService.getRemoteNode(anyInt())).thenReturn(target);
    
            localService.getRemoteNode(1);
            localService.getRemoteNode(2);
            verify(remoteService, times(2)).getRemoteNode(anyInt());
            // 参数1和2的两次调用都会被上面的anyInt()校验到,所以没有未被校验的调用了
            verifyNoMoreInteractions(remoteService);
    
            reset(remoteService);
            localService.getRemoteNode(1);
            localService.getRemoteNode(2);
            verify(remoteService, times(1)).getRemoteNode(1);
            // 参数2的调用不会被上面的校验到,所以执行会抛异常
            verifyNoMoreInteractions(remoteService);
        }
    }
    

    WireMock

    WireMock 是在阅读 kubernetes-client/java 代码时发现的, 在其中有大量使用,它是基于 HTTP API 的 mock 服务框架,和前面提到的 moco 一样,它可以通过文件配置以独立服务启动, 也可以通过代码控制,同时 Spring Cloud Contract WireMock 模块也使得可以在 Spring Boot 应用中使用 WireMock,具体介绍见 Spring Cloud Contract WireMock 。除此之外, WireMock 还提供了在线 mock 服务 MockLab 。下面是 WireMock 在 K8S API 上的示例:

     

    public class K8SApiTest {
        @Rule
        public WireMockRule wireMockRule = new WireMockRule(8000);
    
        private GenericKubernetesApi<V1Job, V1JobList> jobClient;
    
        ApiClient apiClient;
    
    
        @Before
        public void setup() {
            apiClient = new ClientBuilder().setBasePath("http://localhost:" + 8000).build();
            jobClient =
                    new GenericKubernetesApi<>(V1Job.class, V1JobList.class, "batch", "v1", "jobs", apiClient);
        }
    
        // test delete
        @Test
        public void delJob() {
            V1Status status = new V1Status().kind("Status").code(200).message("good!");
            stubFor(
                    delete(urlEqualTo("/apis/batch/v1/namespaces/default/jobs/foo1"))
                            .willReturn(aResponse().withStatus(200).withBody(new Gson().toJson(status))));
    
            KubernetesApiResponse<V1Job> deleteJobResp = jobClient.delete("default", "foo1", null);
            assertTrue(deleteJobResp.isSuccess());
            assertEquals(status, deleteJobResp.getStatus());
            assertNull(deleteJobResp.getObject());
            verify(1, deleteRequestedFor(urlPathEqualTo("/apis/batch/v1/namespaces/default/jobs/foo1")));
        }
    
        @Test
        public void getNs() throws ApiException {
            Configuration.setDefaultApiClient(apiClient);
    
            V1Namespace ns1 = new V1Namespace().metadata(new V1ObjectMeta().name("name"));
    
            stubFor(
                    get(urlEqualTo("/api/v1/namespaces/name"))
                            .willReturn(
                                    aResponse()
                                            .withHeader("Content-Type", "application/json")
                                            .withBody(apiClient.getJSON().serialize(ns1))));
    
            CoreV1Api api = new CoreV1Api();
            V1Namespace ns2 = api.readNamespace("name", null, null, null);
            assertEquals(ns1, ns2);
        }
    }
    

    总结

    以上,就是关于 Mock 服务框架及使用的简单介绍, 详细用法还需要参考相应的文档或源码。关于 Mock 服务框架的选择, 在 《微服务接口:怎么用 Mock 解决混乱的调用关系?》 一文中提到,首先要基于团队的技术栈来选择,这决定了完成服务"替身"的速度;其次,Mock 要方便快速修改和维护,并能马上发挥作用。而关于 Mock 服务的设计,首先要简单,其次处理速度比完美的 Mock 服务更重要;最后,Mock 服务要能轻量化启动,并能容易销毁。

    参考:

    mock

    moco

    SpringMVC 单元测试 - MockMvc

    Mockito & PowerMock

    Wiremock



    作者:深度沉迷学习
    链接:https://www.jianshu.com/p/d3749d8324f4
    来源:简书
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

    展开全文
  • Mock测试

    2021-04-28 17:08:37
    Mock测试 定义 对于单元测试,对于一些不容易构造获取的对象,或者对于尚未开发的模块,创建一个mock对象模拟对象的行为 何时使用mock测试 1、被依赖的对象构造复杂 class A 依赖 class B class B 依赖 class C和...

    Mock测试

    定义

    对于单元测试,对于一些不容易构造获取的对象,或者对于尚未开发的模块,创建一个mock对象模拟对象的行为

    何时使用mock测试

    1、被依赖的对象构造复杂

    class A 依赖 class B
    
    class B 依赖 class Cclass D
    
    class C 依赖 ...
    
    class D 依赖 ...
        
    比如我们想对A进行测试,而此时需要构造大量的class B、C、D等依赖对象,他们的构造过程复杂,此时可以利用mock构造虚拟的B、C、D对象对我们需要的A进行相关的测试
    

    2、被依赖的模块尚未开发完成,而被测对象需要依赖模块的返回值进行测试

    class AController{
        @Autowire
        private AService service;
        
        public void find(){
            service.findAll();
        }
    }
    
    此时我们的AService尚未构造成功,但我们需要对Acontroller进行测试,我们可以用mock虚拟构造Aservice并构造返回值
    

    如何使用mock进行测试

    有一个controller代码如下

    import com.zhongkai.errorbook.service.SourceService;
    import com.zhongkai.errorbook.util.Result;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.*;
    
    import java.util.Map;
    
    /**
     * @author 17715
     * 用来查询以及增加错题来源
     */
    @RestController
    @RequestMapping("/source")
    public class SourceController {
    
        @Autowired
        private SourceService sourceService;
    
        @GetMapping("/selectSource")
        public Result selectSource(String openid){
            return sourceService.selectSource(openid);
        }
    }
    

    此时需要对这个selectController的selectSource进行相关的测试

    假设此时的sourceService还没开发完毕

    此时的mock测试类为

    import com.zhongkai.errorbook.mapper.SourceMapper;
    import com.zhongkai.errorbook.service.SourceService;
    import com.zhongkai.errorbook.service.impl.SourceServiceImpl;
    import com.zhongkai.errorbook.util.CodeMsg;
    import com.zhongkai.errorbook.util.Result;
    import org.apache.commons.collections4.Get;
    import org.junit.Assert;
    import org.junit.Before;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.mockito.InjectMocks;
    import org.mockito.Mock;
    import org.mockito.Mockito;
    import org.mockito.MockitoAnnotations;
    import org.springframework.test.context.junit4.SpringRunner;
    import org.springframework.test.web.servlet.MockMvc;
    import org.springframework.test.web.servlet.ResultMatcher;
    import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
    import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
    import org.springframework.test.web.servlet.result.StatusResultMatchers;
    import org.springframework.test.web.servlet.setup.MockMvcBuilders;
    
    import java.sql.ResultSet;
    
    /**
     * @author simba@onlying.cn
     * @date 2021/4/28 14:08
     */
    @RunWith(SpringRunner.class)
    public class SourceControllerTest {
        /**
         * MockMvc主要进行相关的web controller的测试
         */
        private MockMvc mockMvc;
        /**
         * @Mock表示对SourceController依赖的对象进行注入
         */
        @Mock
        private SourceService service;
        /**
         * @InjectMocks表示将@Mock的对象注入进SourceController
         */
        @InjectMocks
        private SourceController sourceController;
    
        /**
         * 进行相关的MockMvc的初始化
         */
        @Before
        public void init() {
            MockitoAnnotations.initMocks(this);
            mockMvc = MockMvcBuilders.standaloneSetup(sourceController).build();
        }
    
        /**
         * 通过Mockmvc进行相关的接口请求
         * @throws Exception
         */
        @Test
        public void selectTest() throws Exception {
            String openid = "1235664";
            //构造service的虚拟对象,模拟返回值
            Mockito.when(service.selectSource(openid)).thenReturn(new Result(
                    new CodeMsg(111, "测试成功")));
            //模拟对controller接口的调用,并进行相关的参数校验
            this.mockMvc.perform(MockMvcRequestBuilders.get("/source/selectSource").content(openid)
            ).andExpect(MockMvcResultMatchers.status().isOk());
        }
    
        /**
         * 不利用MockMvc进行相关的测试,而是直接调用controller进行相关的测试
         */
        @Test
        public void selectTest2() {
            String openid = "1235664";
            Result result = new Result(new CodeMsg(111, "测试成功"));
             /**
             * 此处也可以用 Mockito.when(service.selectSource(Mockito.anyString())).thenReturn(result);
             * Mockito可以生成任意的字符串、数字、或者复制对象(用any(Class class))生成
             */
            Mockito.when(service.selectSource(openid)).thenReturn(result);
            Result result1 = sourceController.selectSource(openid);
            Assert.assertEquals(result1, result);
        }
    }
    
    

    展示了两种测试controller的方法,对于接口的测试建议还是用第一种进行相关测试

    Mockito.when与Mockito.doAnswer的讲解

    Mockito.when

      Mockito.when(service.selectSource(openid)).thenReturn(new Result(
                    new CodeMsg(111, "测试成功")));
    

    when与thenReturn进行搭配,表示当执行某个方法的时候返回某个返回值,这个返回值在thenReturn中定义,常用于知道返回值的时候,且此时的参数信息不需要额外处理

    Mockito.doAnswer

      Mockito.doAnswer(new Answer() {
                @Override
                public Object answer(InvocationOnMock invocationOnMock) throws Throwable {
                    NirvanaWizardProductDO wizardProductDO = invocationOnMock.getArgument(0);
                    wizardProductDO.setId(1);
                    return null;
                }
            }).when(service).updateStatus(wizardProductDO);
    

    doAnswer表示当service执行updateStatus时返回answer方法返回的值,常用来处理参数信息需要额外处理的时候。

    展开全文
  • mock测试

    2018-06-22 22:46:00
    看到群里有人说mock测试,究竟什么是mock测试呢?开始自己也没明白,查了下相关资料。还是很有必要了解哈:那么mock测试能解决什么问题?mock测试要如何做呢?今天为大家做简单介绍。mock测试就是在测试过程中,对于...

    看到群里有人说mock测试,究竟什么是mock测试呢?开始自己也没明白,查了下相关资料。还是很有必要了解哈:那么mock测试能解决什么问题?mock测试要如何做呢?今天为大家做简单介绍。
    mock测试就是在测试过程中,对于某些不容易构造或者不容易获取的对象,用一个虚拟的对象来创建以便测试的测试方法。(举个例子:一个闹钟根据时间来进行提醒服务,如果过了下午5点钟就播放音频文件提醒大家下班了,

    如果我们要利用真实的对象来测试的话就只能苦苦等到下午五点,然后把耳朵放在音箱旁,我们应该利用mock对象来进行测试,这样我们就可以模拟控制时间了,而不用苦苦等待时钟转到下午5点钟了。)

    PHPMOCK,发现还不错;

    Git下载地址及使用方法:https://gitee.com/gouguoyin/phpmock

    展开全文
  • mock 测试

    千次阅读 2012-11-12 00:14:38
    mock测试 目录 概述 mock对象实例 mock- 展开 概述 mock对象实例 mock- 展开 编辑本段概述:http://baike.baidu.com/view/2445748.htm mock测试  就是在测试过程中,...

    mock测试

    编辑本段概述:http://baike.baidu.com/view/2445748.htm

    mock测试

      就是在测试过程中,对于某些不容易构造或者 不容易获取的对象,用一个虚拟的对象来创建以便测试的测试方法。

    mock对象

      这个虚拟的对象就是mock对象。mock对象就是真实对象在调试期间的代替品。

    mock对象使用范畴

      真实对象具有不可确定的行为,产生不可预测的效果,(如:股票行情,天气预报) 真实对象很难被创建的 真实对象的某些行为很难被触发 真实对象实际上还不存在的(和其他开发小组或者和新的硬件打交道)等等.

    使用mock对象测试的关键步骤

      使用一个接口来描述这个对象 在产品代码中实现这个接口 在测试代码中实现这个接口 在被测试代码中只是通过接口来引用对象,所以它不知道这个引用的对象是真实对象还是mock对象。

    MockObject

      使用Mock Object进行测试,主要是用来模拟那些在应用中不容易构造(如HttpServletRequest必须在Servlet容器中才能构造出来)或者比较复杂的对象(如JDBC中的ResultSet对象)从而使测试顺利进行的工具。
      目前,在Java阵营中主要的Mock测试工具有JMock,MockCreator,Mockrunner,EasyMock,MockMaker等,在微软的.Net阵营中主要是Nmock,.NetMock等。

    编辑本段mock对象实例

      一个闹钟根据时间来进行提醒服务,如果过了下午5点钟就播放音频文件提醒大家下班了,如果我们要利用真实的对象来测试的话就只能苦苦等到下午五点,然后把耳朵放在音箱旁,我们应该利用mock对象[1]来进行测试,这样我们就可以模拟控制时间了,而不用苦苦等待时钟转到下午5点钟了。下面是代码:
      public abstract Environmental {
      boolean playedWav = false;
      public abstract long getTime();
      public abstract void playWavFile(String fileName);
      public abstract boolean wavWasPlayed();
      public abstract void resetWav();
      }
      真实的实现代码:
      public class SystemEnvironment extends Environmental {
      public long getTime() {
      return System.currentTimeMillis();
      }
      public void playWavFile(String fileName) {
      playedWav = true;
      }
      public boolean wavWasPlayed() {
      return playedWav; }
      public void resetWav() {
      playedWav = false;
      }
      }
      下面是mock对象:
      public class MockSystemEnvironment extends Environmental {
      private long currentTime;
      public long getTime() {
      return currentTime;
      }
      public void setTime(long currentTime) {
      this.currentTime = currentTime;
      }
      public void playWavFile(String fileName) {
      playedWav = true;
      }
      public boolean wavWasPlayed() {
      return playedWav;
      }
      public void resetWav() {
      playedWav = false;
      }
      }

    编辑本段mock-

      复合形 comb.form
      1. 表示"模仿","假的"
      mock测试可翻译为mock-test
    展开全文
  • MOCK测试

    2011-09-02 09:26:56
    mock测试:就是在测试过程中,对于某些不容易构造或者 不容易获取的对象,用一个虚拟的对象【mock对象】来创建以便测试的测试方法。 mock对象:这个虚拟的对象就是mock对象。 mock对象就是真实对象在调试期间的...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 8,760
精华内容 3,504
关键字:

mock测试