学习如何使用本教程中提供的工具,并在 Spring Boot 环境中编写单元测试和集成测试。

1. 概览

本文中,我们将了解如何编写单元测试并将其集成在 Spring Boot 环境中。你可在网上找到大量关于这个主题的教程,但很难在一个页面中找到你需要的所有信息。我经常注意到初级开发人员混淆了单元测试和集成测试的概念,特别是在谈到 Spring 生态系统时。我将尝试讲清楚不同注解在不同上下文中的用法。

2. 单元测试 vs. 集成测试

维基百科是这么说 单元测试 的:

在计算机编程中,单元测试是一种软件测试方法,用以测试源代码的单个单元、一个或多个计算机程序模块的集合以及相关的控制数据、使用过程和操作过程,以确定它们是否适合使用。

集成测试

“集成测试(有时也称集成和测试,缩写为 I&T)是软件测试的一个阶段,在这个阶段中,各个软件模块被组合在一起来进行测试。”

简而言之,当我们在做单元测试时,只是测试了一个代码单元,每次只测试一个方法,不包括与正测试组件相交互的其他所有组件。

另一方面,在集成测试中,我们测试各组件之间的集成。由于单元测试,我们可知这些组件行为与所需一致,但不清楚它们是如何在一起工作的。这就是集成测试的职责。

3. Java 单元测试

所有 Java 开发者都知道 JUnit 是执行单元测试的主要框架。它提供了许多注解来对期望进行断言。

Hamcrest 是一个用于软件测试的附加框架。Hamcrest 允许使用现有的 matcher 类来检查代码中的条件,还允许自定义 matcher 实现。要在 JUnit 中使用 Hamcrest matcher,必须使用 assertThat 语句,后跟一个或多个 matcher。

在这里,你可以看到使用这两种框架的简单测试:

import static org.hamcrest.CoreMatchers.allOf;
import static org.hamcrest.CoreMatchers.anyOf;
import static org.hamcrest.CoreMatchers.both;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.everyItem;
import static org.hamcrest.CoreMatchers.hasItems;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.sameInstance;
import static org.hamcrest.CoreMatchers.startsWith;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue; import java.util.Arrays; import org.hamcrest.core.CombinableMatcher;
import org.junit.Test; public class AssertTests {
@Test
public void testAssertArrayEquals() {
byte[] expected = "trial".getBytes();
byte[] actual = "trial".getBytes();
assertArrayEquals("failure - byte arrays not same", expected, actual);
} @Test
public void testAssertEquals() {
assertEquals("failure - strings are not equal", "text", "text");
} @Test
public void testAssertFalse() {
assertFalse("failure - should be false", false);
} @Test
public void testAssertNotNull() {
assertNotNull("should not be null", new Object());
} @Test
public void testAssertNotSame() {
assertNotSame("should not be same Object", new Object(), new Object());
} @Test
public void testAssertNull() {
assertNull("should be null", null);
} @Test
public void testAssertSame() {
Integer aNumber = Integer.valueOf(768);
assertSame("should be same", aNumber, aNumber);
} // JUnit Matchers assertThat
@Test
public void testAssertThatBothContainsString() {
assertThat("albumen", both(containsString("a")).and(containsString("b")));
} @Test
public void testAssertThatHasItems() {
assertThat(Arrays.asList("one", "two", "three"), hasItems("one", "three"));
} @Test
public void testAssertThatEveryItemContainsString() {
assertThat(Arrays.asList(new String[] { "fun", "ban", "net" }), everyItem(containsString("n")));
} // Core Hamcrest Matchers with assertThat
@Test
public void testAssertThatHamcrestCoreMatchers() {
assertThat("good", allOf(equalTo("good"), startsWith("good")));
assertThat("good", not(allOf(equalTo("bad"), equalTo("good"))));
assertThat("good", anyOf(equalTo("bad"), equalTo("good")));
assertThat(7, not(CombinableMatcher.<Integer> either(equalTo(3)).or(equalTo(4))));
assertThat(new Object(), not(sameInstance(new Object())));
} @Test
public void testAssertTrue() {
assertTrue("failure - should be true", true);
}
}

4. 介绍我们的案例

让我们来写一个简单的程序吧。其目的是为漫画提供一个基本的搜索引擎。

4.1. Maven 依赖

首先,需要添加一些依赖到我们的工程中。

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.20</version>
<scope>provided</scope>
</dependency>

4.2. 定义 Model

我们的模型非常简单,只有两个类组成:Manga 和 MangaResult

4.2.1. Manga 类

Manga 类表示系统检索到的 Manga 实例。使用 Lombok 来减少样板代码。

package com.mgiglione.model;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter; @Getter @Setter @NoArgsConstructor @AllArgsConstructor @Builder
public class Manga {
private String title;
private String description;
private Integer volumes;
private Double score;
}

4.2.2. MangaResult

MangaResult 类是包含了一个 Manga List 的包装类。

package com.mgiglione.model;

import java.util.List;

import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter; @Getter @Setter @NoArgsConstructor
public class MangaResult {
private List<Manga> result;
}

4.3. 实现 Service

为实现本 Service,我们将使用由 Jikan Moe 提供的免费 API 接口。

RestTemplate 是用来对 API 进行发起 REST 调用的 Spring 类。

package com.mgiglione.service;

import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate; import com.mgiglione.model.Manga;
import com.mgiglione.model.MangaResult; @Service
public class MangaService { Logger logger = LoggerFactory.getLogger(MangaService.class);
private static final String MANGA_SEARCH_URL="http://api.jikan.moe/search/manga/"; @Autowired
RestTemplate restTemplate; public List<Manga> getMangasByTitle(String title) {
return restTemplate.getForEntity(MANGA_SEARCH_URL+title, MangaResult.class).getBody().getResult();
} }

4.4. 实现 Controller

下一步就是写一个暴露了两个端点的 REST Controller,一个是同步的,一个是异步的,其仅用于测试目的。该 Controller 使用了上面定义的 Service。

package com.mgiglione.controller;

import java.util.List;
import java.util.concurrent.CompletableFuture; import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController; import com.mgiglione.model.Manga;
import com.mgiglione.service.MangaService; @RestController
@RequestMapping(value = "/manga")
public class MangaController { Logger logger = LoggerFactory.getLogger(MangaController.class); @Autowired
private MangaService mangaService; @RequestMapping(value = "/async/{title}", method = RequestMethod.GET)
@Async
public CompletableFuture<List<Manga>> searchASync(@PathVariable(name = "title") String title) {
return CompletableFuture.completedFuture(mangaService.getMangasByTitle(title));
} @RequestMapping(value = "/sync/{title}", method = RequestMethod.GET)
public @ResponseBody <List<Manga>> searchSync(@PathVariable(name = "title") String title) {
return mangaService.getMangasByTitle(title);
} }

4.5. 启动并测试系统

mvn spring-boot:run

然后,Let’s try it:

curl http://localhost:8080/manga/async/ken
curl http://localhost:8080/manga/sync/ken

示例输出:

{
"title":"Rurouni Kenshin: Meiji Kenkaku Romantan",
"description":"Ten years have passed since the end of Bakumatsu, an era of war that saw the uprising of citizens against the Tokugawa shogunate. The revolutionaries wanted to create a time of peace, and a thriving c...",
"volumes":28,
"score":8.69
},
{
"title":"Sun-Ken Rock",
"description":"The story revolves around Ken, a man from an upper-class family that was orphaned young due to his family's involvement with the Yakuza; he became a high school delinquent known for fighting. The only...",
"volumes":25,
"score":8.12
},
{
"title":"Yumekui Kenbun",
"description":"For those who suffer nightmares, help awaits at the Ginseikan Tea House, where patrons can order much more than just Darjeeling. Hiruko is a special kind of a private investigator. He's a dream eater....",
"volumes":9,
"score":7.97
}

5. Spring Boot 应用的单元测试

Spring Boot 提供了一个强大的类以使测试变得简单:@SpringBootTest 注解

可以在基于 Spring Boot 运行的测试类上指定此注解。

除常规 Spring TestContext Framework 之外,其还提供以下功能:

  • 当 @ContextConfiguration (loader=…) 没有特别声明时,使用 SpringBootContextLoader 作为默认 ContextLoader。
  • 在未使用嵌套的 @Configuration 注解,且未显式指定相关类时,自动搜索 @SpringBootConfiguration。
  • 允许使用 Properties 来自定义 Environment 属性。
  • 对不同的 Web 环境模式提供支持,包括启动在已定义或随机端口上的完全运行的 Web 服务器的功能。
  • 注册 TestRestTemplate 和 / 或 WebTestClient Bean,以便在完全运行在 Web 服务器上的 Web 测试中使用。

此处,我们仅有两个组件需要测试:MangaService 和 MangaController

5.1. 对 MangaService 进行单元测试

为了测试 MangaService,我们需要将其与外部组件隔离开来。本例中,只需要一个外部组件:RestTemplate,我们用它来调用远程 API。

我们需要做的是模拟 RestTemplate Bean,并让它始终以固定的给定响应进行响应。Spring Test 结合并扩展了 Mockito 库,通过 @MockBean 注解,我们可以配置模拟 Bean。

package com.mgiglione.service.test.unit;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when; import java.io.IOException;
import java.util.List; import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.client.RestTemplate;
import static org.assertj.core.api.Assertions.assertThat; import com.mgiglione.model.Manga;
import com.mgiglione.model.MangaResult;
import com.mgiglione.service.MangaService;
import com.mgiglione.utils.JsonUtils; @RunWith(SpringRunner.class)
@SpringBootTest
public class MangaServiceUnitTest { @Autowired
private MangaService mangaService; // MockBean is the annotation provided by Spring that wraps mockito one
// Annotation that can be used to add mocks to a Spring ApplicationContext.
// If any existing single bean of the same type defined in the context will be replaced by the mock, if no existing bean is defined a new one will be added.
@MockBean
private RestTemplate template; @Test
public void testGetMangasByTitle() throws IOException {
// Parsing mock file
MangaResult mRs = JsonUtils.jsonFile2Object("ken.json", MangaResult.class);
// Mocking remote service
when(template.getForEntity(any(String.class), any(Class.class))).thenReturn(new ResponseEntity(mRs, HttpStatus.OK));
// I search for goku but system will use mocked response containing only ken, so I can check that mock is used.
List<Manga> mangasByTitle = mangaService.getMangasByTitle("goku");
assertThat(mangasByTitle).isNotNull()
.isNotEmpty()
.allMatch(p -> p.getTitle()
.toLowerCase()
.contains("ken"));
} }

5.2. 对 MangaController 进行单元测试

正如在 MangaService 的单元测试中所做的那样,我们需要隔离组件。在这种情况下,我们需要模拟 MangaService Bean。

然后,我们还有一个问题……Controller 部分是管理 HttpRequest 的系统的一部分,因此我们需要一个系统来模拟这种行为,而非启动完整的 HTTP 服务器。

MockMvc 是执行该操作的 Spring 类。其可以以不同的方式进行设置:

  1. 使用 Standalone Context
  2. 使用 WebApplication Context
  3. 让 Spring 通过在测试类上使用 @SpringBootTest、@AutoConfigureMockMvc 这些注解来加载所有的上下文,以实现自动装配
  4. 让 Spring 通过在测试类上使用 @WebMvcTest 注解来加载 Web 层上下文,以实现自动装配
package com.mgiglione.service.test.unit;

import static org.hamcrest.Matchers.is;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.asyncDispatch;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.request;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup; import java.util.ArrayList;
import java.util.List; import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.web.context.WebApplicationContext; import com.mgiglione.controller.MangaController;
import com.mgiglione.model.Manga;
import com.mgiglione.service.MangaService; @SpringBootTest
@RunWith(SpringRunner.class)
public class MangaControllerUnitTest { MockMvc mockMvc; @Autowired
protected WebApplicationContext wac; @Autowired
MangaController mangaController; @MockBean
MangaService mangaService; /**
* List of samples mangas
*/
private List<Manga> mangas; @Before
public void setup() throws Exception {
this.mockMvc = standaloneSetup(this.mangaController).build();// Standalone context
// mockMvc = MockMvcBuilders.webAppContextSetup(wac)
// .build();
Manga manga1 = Manga.builder()
.title("Hokuto no ken")
.description("The year is 199X. The Earth has been devastated by nuclear war...")
.build();
Manga manga2 = Manga.builder()
.title("Yumekui Kenbun")
.description("For those who suffer nightmares, help awaits at the Ginseikan Tea House, where patrons can order much more than just Darjeeling. Hiruko is a special kind of a private investigator. He's a dream eater....")
.build();
mangas = new ArrayList<>();
mangas.add(manga1);
mangas.add(manga2);
} @Test
public void testSearchSync() throws Exception {
// Mocking service
when(mangaService.getMangasByTitle(any(String.class))).thenReturn(mangas);
mockMvc.perform(get("/manga/sync/ken").contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$[0].title", is("Hokuto no ken")))
.andExpect(jsonPath("$[1].title", is("Yumekui Kenbun")));
} @Test
public void testSearchASync() throws Exception {
// Mocking service
when(mangaService.getMangasByTitle(any(String.class))).thenReturn(mangas);
MvcResult result = mockMvc.perform(get("/manga/async/ken").contentType(MediaType.APPLICATION_JSON))
.andDo(print())
.andExpect(request().asyncStarted())
.andDo(print())
// .andExpect(status().is2xxSuccessful()).andReturn();
.andReturn();
// result.getRequest().getAsyncContext().setTimeout(10000);
mockMvc.perform(asyncDispatch(result))
.andDo(print())
.andExpect(status().isOk())
.andExpect(jsonPath("$[0].title", is("Hokuto no ken")));
}
}

正如在代码中所看到的那样,选择第一种解决方案是因为其是最轻量的一个,并且我们可以对 Spring 上下文中加载的对象有更好的治理。

在异步测试中,必须首先通过调用服务,然后启动 asyncDispatch 方法来模拟异步行为。

6. Spring Boot 应用的集成测试

对于集成测试,我们希望提供下游通信来检查我们的主要组件。

6.1. 对 MangaService 进行集成测试

这个测试也是非常简单的。我们不需要模拟任何东西,因为我们的目的就是要调用远程 Manga API。

package com.mgiglione.service.test.integration;

import static org.assertj.core.api.Assertions.assertThat;

import java.util.List;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner; import com.mgiglione.model.Manga;
import com.mgiglione.service.MangaService; @RunWith(SpringRunner.class)
@SpringBootTest
public class MangaServiceIntegrationTest { @Autowired
private MangaService mangaService; @Test
public void testGetMangasByTitle() {
List<Manga> mangasByTitle = mangaService.getMangasByTitle("ken");
assertThat(mangasByTitle).isNotNull().isNotEmpty();
} }

6.2. 对 MangaController 进行集成测试

这个测试和单元测试很是相似,但在这个案例中,我们无需再模拟 MangaService。

package com.mgiglione.service.test.integration;

import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.is;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.asyncDispatch;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.request;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup; import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.web.context.WebApplicationContext; import com.mgiglione.controller.MangaController; @SpringBootTest
@RunWith(SpringRunner.class)
public class MangaControllerIntegrationTest { // @Autowired
MockMvc mockMvc; @Autowired
protected WebApplicationContext wac; @Autowired
MangaController mangaController; @Before
public void setup() throws Exception {
this.mockMvc = standaloneSetup(this.mangaController).build();// Standalone context
// mockMvc = MockMvcBuilders.webAppContextSetup(wac)
// .build();
} @Test
public void testSearchSync() throws Exception {
mockMvc.perform(get("/manga/sync/ken").contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.*.title", hasItem(is("Hokuto no Ken"))));
} @Test
public void testSearchASync() throws Exception {
MvcResult result = mockMvc.perform(get("/manga/async/ken").contentType(MediaType.APPLICATION_JSON))
.andDo(print())
.andExpect(request().asyncStarted())
.andDo(print())
.andReturn();
mockMvc.perform(asyncDispatch(result))
.andDo(print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.*.title", hasItem(is("Hokuto no Ken"))));
} }

7. 结论

我们已经了解了在 Spring Boot 环境下单元测试和集成测试的主要不同,了解了像 Hamcrest 这样简化测试编写的框架。当然,也可以在我的 GitHub 仓库 里找到所有代码。

原文:https://dzone.com/articles/unit-and-integration-tests-in-spring-boot-2

作者:Marco Giglione

译者:万想


9月福利,关注公众号



后台回复:004,领取8月翻译集锦!



往期福利回复:001,002, 003即可领取!

Spring Boot 的单元测试和集成测试的更多相关文章

  1. Spring Boot学习——单元测试

    本随笔记录使用Spring Boot进行单元测试,主要是Service和API(Controller)进行单元测试. 一.Service单元测试 选择要测试的service类的方法,使用idea自动创 ...

  2. Spring Boot干货系列:(十二)Spring Boot使用单元测试(转)

    前言这次来介绍下Spring Boot中对单元测试的整合使用,本篇会通过以下4点来介绍,基本满足日常需求 Service层单元测试 Controller层单元测试 新断言assertThat使用 单元 ...

  3. Spring Boot 的单元测试

    Spring Boot 的单元测试 引入依赖 testCompile group: 'org.springframework.boot', name: 'spring-boot-starter-tes ...

  4. 学习 Spring Boot:(二十九)Spring Boot Junit 单元测试

    前言 JUnit 是一个回归测试框架,被开发者用于实施对应用程序的单元测试,加快程序编制速度,同时提高编码的质量. JUnit 测试框架具有以下重要特性: 测试工具 测试套件 测试运行器 测试分类 了 ...

  5. Spring Boot使用单元测试

    一.Service层单元测试: 代码如下: package com.dudu.service;import com.dudu.domain.LearnResource;import org.junit ...

  6. Spring Boot Mock单元测试学习总结

    单元测试的方法有很多种,比如使用Postman.SoapUI等工具测试,当然,这里的测试,主要使用的是基于RESTful风格的SpringMVC的测试,我们可以测试完整的Spring MVC流程,即从 ...

  7. Spring Boot 2 单元测试

    开发环境:IntelliJ IDEA 2019.2.2Spring Boot版本:2.1.8 IDEA新建一个Spring Boot项目后,pom.xml默认包含了Web应用和单元测试两个依赖包.如下 ...

  8. (27)Spring Boot Junit单元测试【从零开始学Spring Boot】

    Junit这种老技术,现在又拿出来说,不为别的,某种程度上来说,更是为了要说明它在项目中的重要性. 那么先简单说一下为什么要写测试用例 1. 可以避免测试点的遗漏,为了更好的进行测试,可以提高测试效率 ...

  9. (转)Spring Boot Junit单元测试

    场景:在项目开发中要测试springboot工程中一个几个dao和service的功能是否正常,初期是在web工程中进行要素的录入,工作量太大.使用该单元测试大大减小了工作强度. Junit这种老技术 ...

随机推荐

  1. Spring源码剖析1:初探Spring IOC核心流程

    本文大致地介绍了IOC容器的初始化过程,只列出了比较重要的过程和代码,可以从中看出IOC容器执行的大致流程. 接下来的文章会更加深入剖析Bean容器如何解析xml,注册和初始化bean,以及如何获取b ...

  2. 一文道尽JavaScript 20年的发展史

      作者介绍:Andrew Montalenti是Parse.ly的CTO, 一个长期的Python爱好者,以及初创公司和其他项目的创始人. 原文链接:https://amontalenti.com/ ...

  3. [Python] socket实现TFTP上传和下载

    一.说明 本文主要基于socket实现TFTP文件上传与下载. 测试环境:Win10/Python3.5/tftpd64. tftpd下载:根据自己的环境选择下载,地址 :http://tftpd32 ...

  4. 第一篇 特征提取以及openvslam中的相关实现详解

    参考尺度空间理论 金字塔 当用一个机器视觉系统分析未知场景时,计算机没有办法预先知道图像中物体尺度,因此,我们需要同时考虑图像在多尺度下的描述,获知感兴趣物体的最佳尺度.所以在很多时候,我们会在将图像 ...

  5. centos7安装使用docker-tomcat-mysql

    windows安装centos虚拟机 下载安装 virtualBox下载 https://mirrors.tuna.tsinghua.edu.cn/help/virtualbox/ centos7镜像 ...

  6. Android定时锁屏功能实现(AlarmManager定时部分)

    菜鸟入坑记——第一篇 关键字:AlarmManager 一.AlarmManager简介: 参考网址:https://www.jianshu.com/p/8a2ce9d02640        参考网 ...

  7. atcoder C - Snuke and Spells(模拟+思维)

    题目链接:http://agc017.contest.atcoder.jp/tasks/agc017_c 题解:就是简单的模拟一下就行.看一下代码就能理解 #include <iostream& ...

  8. CF1007B Pave the Parallelepiped 容斥原理

    Pave the Parallelepiped time limit per test 2 seconds memory limit per test 256 megabytes input stan ...

  9. codeforces 245 D. Restoring Table(位运算+思维)

    题目链接:http://codeforces.com/contest/245/problem/D 题意:给出一个矩阵b,b[i][j]=a[i]&a[j],b[i][i]=-1.然后求a[i] ...

  10. HDFS 读写流程-英

    HDFS 文件读取流程 The client opens the file it wishes to read by calling open() on the FileSystem object, ...