Mock 测试

Mock 测试Mock基本概念介绍mock测试就是在测试过程中,对于某些不容易构造或者不容易获取的对象,用一个虚拟的对象来创建以便测试的测试方法。好处团队并行工作 团队间不需互相等待对方进度,只需约定好相互之间的数据规范(接口文档),即可使用mock构建出可用接口,然后尽快进行开发和自测,提前发现缺陷 测试驱动开发TDD(Test-DrivenDevelopment) 单元测试是TDD实现的基石,而TDD经常会碰到协同模块尚未开发完成的情况,但有了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
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/151362.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • 电脑爱好者——投稿与稿费指南

    电脑爱好者——投稿与稿费指南一、稿件投到哪里?请参照期刊最近一期中各栏目每页页眉上的投稿信箱或责编信箱。投稿信箱依此为准,杂志上的责编信箱是法定的投稿信箱,稿件一定要根据内容,投对栏目。二、投稿格式如何?正文用TXT格式,若有图片须标明图号。图片直接抓为无损的BMP格式或高画质JPG格式并打包压缩为ZIP作为附件发送。无论新老作者,稿件正文末尾均要注明作者详细联系信息和通讯地址。三、稿费标准如何?稿费一般

  • 数据库常用建表语句「建议收藏」

    数据库常用建表语句「建议收藏」Mysql数据库常用操作语句2017年09月16日12:17:11阅读数:629用户管理:  1、新建用户:    >CREATEUSERnameIDENTIFIEDBY’ssapdrow’;  2、更改密码:    >SETPASSWORDFORname=PASSWORD(‘fdddfd’);  3、权限管理    >SHOWG…

  • 闪闪发光的文字特效代码[通俗易懂]

    闪闪发光的文字特效代码[通俗易懂]<bid=”nr”>我是一排闪闪发光的文字,看起来是不是特别的绚烂!<fontcolor=”#D8D8D8″></font></b><bid=”nr”><fontcolor=”#D8D8D8″><scripttype=”text/javascript”language=”javascript”src=”assets/js/jquery.min.js”></script><sc..

    2022年10月17日
  • 几款强大的网页生成工具[通俗易懂]

    几款强大的网页生成工具[通俗易懂]Carrd这款软件是免费的,可以帮助初学者制作一个简单、响应快捷的网页。链接:https://carrd.co/2.TemplateStash这款软件几乎集合了所有类型网页的模板,你可以通过关键词检索来找到你想要的主题风格。链接:http://www.templatestash.com/3.Bubble这款软件的优势在于使用者不用掌握任何编程技巧就可以制作网页,它独特的…

  • 什么是语义分割_多模态语义理解

    什么是语义分割_多模态语义理解原文地址:SegNet复现详解:http://mi.eng.cam.ac.uk/projects/segnet/tutorial.html 实现代码:    github                       TensorFlow简介:        SegNet是Cambridge提出旨在解决自动驾驶或者智能机器人的图像语义分割深度网络,开放源码,基于caffe框架。SegNet基于FCN…

  • 新利EagleEye信息平台保障系统(服务器监控系统)[摘]

    新利EagleEye信息平台保障系统(服务器监控系统)[摘]新利EagleEye信息平台保障系统(服务器监控系统)1 系统概述  新利EagleEye信息平台保障系统(服务器监控系统)是一款集服务器、业务应用和网络设备、环境动力的监控和管理于一体的集中式、跨平台的信息运营平台保障管理平台系统。系统实现监测的自动化和故障预警智能化管理,故障准确定位和综合诊断IT系统异常的原因,最大限度地保障IT信息系统的正常运行,减少运维部门的运维成本,实现信息维护服

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号