SEO基础

SEO基础

Products

当前位置:首页 > SEO基础 >

二次开发网站时,客户是否需提供源代码?

96SEO 2026-02-19 12:49 0


ot-starter-test

2.1、配置类型的注解2.2、Mock类型的注解2.3、自动配置类型的注解2.4、启动测试类型的注解2.5、相似注解的区别和联系

二次开发网站时,客户是否需提供源代码?

3.1、单元测试3.2、集成测试

1、测试逻辑2、MockMvcBuilder3、MockMvcRequestBuilders4、ResultActions5、ResultMatchers6、MvcResult

5、业务代码6、分层测试

6.1、Dao层测试6.2、Service层测试6.3、Controller层测试

7、JSON接口测试

SpringBoot对单元测试的支持在于提供了一系列注解和工具的集成它们是通过两个项目提供的

spring-boot-test项目包含核心功能spring-boot-test-autoconfigure项目支持自动配置

通常情况下我们通过spring-boot-starter-test的Starter来引入SpringBoot的核心支持项目以及单元测试项目以及单元测试库。

spring-boot-starter-test包含的类库如下

Test

Test为SpringBoot应用提供集成测试和工具支持AssertJ支持流式断言的Java测试框架Hamcrest一个匹配器库Mockito一个Java

Mock框架JSONassert一个针对JSON的断言库JsonPath一个JSON

XPath库

如果SpringBoot提供的基础类无法满足业务需求我们也可以自行添加依赖。

依赖注入的优点之一就是可以轻松使用单元测试。

这种方式可以直接通过new来创建对象而不需要涉及Spring。

当然也可以通过模拟对象来替换真实依赖。

如果需要集成测试比如使用Spring的ApplicationContextSpring同样能够提供无须部署应用程序或连接到其它基础环境的集成测试。

而SpringBoot应用本身就是一个ApplicationContext因此除了正常使用Spring上下文进行测试无须执行其它操作。

Maven依赖

dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-test/artifactIdscopetest/scope

/dependency2、常用注解

以Test结尾的注解具有加载applicationContext的能力

自动配置类型

以AutoConfigure开头的注解具有加载测试支持功能的能力

TestComponent

该注解为另一种Component在语义上用来指定某个Bean是专门用于测试的

该注解适用与测试代码和正式混合在一起时不加载被该注解描述的Bean使用不多

TestConfiguration

该注解是另一种TestComponent它用于补充额外的Bean或覆盖已存在的Bean

TypeExcludeFilters

用来排除TestConfiguration和TestComponent

可用于覆盖EnableAutoCOnfiguration与ImportAutoConfiguration结合使用以限制所加载的自动配置类

PropertyMapping

定义AutoConfigure注解中用到的变量名称例如在AutoConfigureMockMvc中定义名为spring.test.mockmvc.webclient.enabled的变量

一般不使用

使用SpringBootApplication启动测试或者生产代码被TestComponent描述的Bean会自动被排除掉。

如果不是则需要向SpringBootApplication添加TypeExcludeFilter。

MockBean

MockBean和SpyBean这两个注解在mockito框架中本来已经存在且功能基本相同。

Spring

Boot

Test又定义一份重复的注解目的在于使MockBean和SpyBean被ApplicationContext管理从而方便使用。

MockBean和SpyBean功能非常相似都能模拟方法的各种行为。

不同之处在于MockBean是全新的对象跟正式对象没有关系而SpyBean与正式对象紧密联系可以模拟正式对象的部分方法没有被模拟的方法仍然可以运行正式代码。

AutoConfigureJdbc

AutoConfigureMockRestServiceServer

自动配置WebClient

这些注解可以搭配Test使用用于开启在Test中未自动配置的功能。

例如SpringBootTest和AutoConfigureMockMvc组合后就可以注入org.springframework.test.web.servlet.MockMvc。

自动配置类型有两种使用方式

在功能测试即使用SpringBootTest时显示添加。

一般在切片测试中被隐式使用例如WebMvcTest注解时隐式添加了AutoConfigureCache、AutoConfigureWebMvc和AutoConfigureMockMvc。

所有的*Test注解都被BootstrapWith注解它们可以启动ApplicationContext是测试的入口所有的测试类必须声明一个*Test注解。

SpringBootTest

自动侦测并加载SpringBootApplication或SpringBootConfiguration中的配置默认web环境为Mock不见听任务端口

DataRedisTest

测试对Redis操作自动扫描被RedisHash描述的类并配置Spring

Data

测试基于JPA的数据库操作同时提供了TestEntityManager替代JPA的EntityManager

DataJdbcTest

除了SpringBootTest之外的注解都是用来进行切面测试的他们会默认导入一些自动配置点击查看官方文档。

一般情况推荐使用SpringBootTest而非其它切片测试的注解简单有效。

若某次改动仅涉及特定切片可以考虑使用切片测试。

SpringBootTest是这些注解中最常用的一个其中包含的配置项如下

value指定配置属性properties指定配置属性和value意义相同classes指定配置类等同于ContextConfiguration中的class若没有显示指定将查找嵌套的Configuration类然后返回到SpringBootConfiguration搜索配置webEnviroment指定web环境可选值如下

MOCK此值为默认值该类型提供一个mock环境此时内嵌的服务servlet容器并没有真正启动也不会监听web端口RANDOM_PORT启动一个真实的web服务监听一个随机端口DEFINED_PORT启动一个真实的web服务监听一个定义好的端口从配置中读取NONE启动一个非web的ApplicationContext既不提供mock环境也不提供真实的web服务

TestComment和CommentTestComment是另一种Component在语义上用来指定某个Bean是专门用于测试的。

使用SpringBootApplication服务时TestComponent会被自动排除TestConfiguration和ConfigurationTestConfiguration是Spring

Boot

Framework提供的。

TestConfiguration实际上是也是一种TestComponent只是这个TestComponent专门用来做配置用。

TestConfiguration和Configuration不同它不会阻止SpringBootTest的查找机制相当于是对既有配置的补充或覆盖。

SpringBootTest和WebMvcTest或*Test都可以启动Spring的ApplicationContext

SpringBootTest自动侦测并加载SpringBootApplication或SpringBootConfiguration中的配置WebMvcTest不侦测配置只是默认加载一些自动配置。

SpringBootTest测试范围一般比WebMvcTest大。

MockBean和SpyBean都能模拟方法的各种行为。

不同之处在于MockBean是全新的对象跟正式对象没有关系而SpyBean与正式对象紧密联系可以模拟正式对象的部分方法没有被模拟的方法仍然可以运行正式代码。

整体上Spring

单元测试一般面向方法编写一般业务代码时测试成本较大。

涉及到的注解有Test。

切片测试一般面向于测试的边界功能介于单元测试和功能测试之间。

涉及到的注解有WebMvcTest等。

主要就是对于Controller的测试分离了Service层这里就涉及到Mock控制层所依赖的组件了。

功能测试一般面向某个完整的业务功能同时也可以使用切面测试中mock能力推荐使用。

涉及到的注解有SpringBootTest等。

3.1、单元测试

Environment但是不会启动内置的server。

这点从日志中没有打印Tomcat

started

User();user.setName(tom);user.setAge(18);user.setHeight(1.88);Assertions.assertThat(userMapper.add(user)).isEqualTo(1);}

}3.2、集成测试

//此时将会加载ApplicationContext并启动ServerServer监听在随机端口上。

SpringBootTest.WebEnvironment.RANDOM_PORT)

public

{Assertions.assertThat(port).isGreaterThan(1024);}

来指定server侦听应用程序配置的端口默认为8080。

不过这种指定端口的方式很少使用因为如果本地同时启动应用时会导致端口冲突。

4、MockMvc

MockMvc可以做到不启动项目工程就可以对结构进行测试。

MockMvc实现了对HTTP请求的模拟能够直接使用网络的形式转换到Controller的调用这样可以使得测试速度快、不依赖网络环境同时提供了一套验证的工具使得请求的验证同一而且方便。

4.1、简单示例

创建一个简单的TestController提供一个方法返回一个字符串

RestController

{//mockMvc.perform执行一个请求mockMvc.perform(MockMvcRequestBuilders//构造请求.get(/mock)//设置返回值类型.accept(MediaType.APPLICATION_JSON)//添加请求参数.param(name,

tom))//添加执行完成后的断言.andExpect(MockMvcResultMatchers.status().isOk()).andExpect(MockMvcResultMatchers.content().string(Hello

tom!))//添加一个结果处理器此处打印整个响应结果信息.andDo(MockMvcResultHandlers.print());}

}运行测试输出

pers.zhang.controller.TestControllerMethod

pers.zhang.controller.TestController#mock(String)Async:Async

started

nullMockHttpServletResponse:Status

200Error

[Content-Type:application/json,

type

[]AutoConfigureMockMvc注解提供了自动配置MockMvc的功能。

Autowired注入MockMvc对象。

MockMvc对象可以通过接口M哦查看Mv吃Builder的实现类获得。

该接口提供一个唯一的build方法来构造MockMvc。

主要有两个实现类

StandaloneMockMvcBuilder独立安装DefaultMockMvcBuilder集成Web环境测试并不会真正的web环境而是通过相应的Mock

API进行模拟测试无须启动服务器

MockMvcBuilders提供了对应的standaloneSetup和webAppContextSetup两种创建方法在使用时直接调用即可默认使用DefaultMOckMvcBuilder。

整个单元测试包含一下步骤

准备测试环境执行MockMvc请求添加验证断言添加结果处理器得到MvcResult进行自定义断言/进行下一步的异步请求卸载测试环境

4.2、自动配置

AutoConfigureMockMvc提供了自动配置MockMvc的功能源码如下

Target({

Retention(RetentionPolicy.RUNTIME)

Documented

PropertyMapping(spring.test.mockmvc)

public

{//是否应向MockMvc注册来自应用程序上下文的filter默认trueboolean

addFilters()

true;//每次MockMvc调用后应如何打印MvcResult信息PropertyMapping(skip

SkipPropertyMapping.ON_DEFAULT_VALUE)MockMvcPrint

print()

MockMvcPrint.DEFAULT;//如果MvcResult仅在测试失败时才打印信息。

默认true则表示只在失败时打印boolean

printOnlyOnFailure()

true;//当HtmlUnit在类路径上时是否应该自动配置WebClient。

默认为truePropertyMapping(webclient.enabled)boolean

webClientEnabled()

true;//当Selenium位于类路径上时是否应自动配置WebDriver。

默认为truePropertyMapping(webdriver.enabled)boolean

webDriverEnabled()

true;}在AutoConfigureMockMvc的源码中我们重点看它组合的ImportAutoConfiguration注解。

该注解同样是SpringBoot自动配置项目提供的其功能类似EnableAutoConfiguration但又略有区别。

ImportAutoConfiguration同样用于导入自动配置类不仅可以像EnableAutoConfiguration那样排除指定的自动配置配置类还可以指定使用哪些自动配置类这是它们之间的重要区别之一。

另外ImportAutoConfiguration使用的排序规则与EnableAutoConfiguration的相同通常情况下建议优先使用EnableAutoConfiguration注解进行自动配置。

但在单元测试中则可考虑优先使用ImportAutoCOnfiguration。

源码如下

Retention(RetentionPolicy.RUNTIME)

Documented

Import(ImportAutoConfigurationImportSelector.class)

public

{//指定引入的自动配置类AliasFor(classes)Class?[]

value()

{};//指定引入的自动配置类。

如果为空则使用META-INF/spring.factories中注册的指定类//其中spring.factories中注册的key为被该注解的类的全限定名称AliasFor(value)Class?[]

classes()

{};}通过value属性提供了指定自动配置类的功能可以通过细粒度控制根据需要引入相应功能的自动配置。

没有EnableAutoConfiguration一次注入全局生效的特性但是有了指定的灵活性。

更值得注意的是classes属性它也是用来指定自动配置类的但它的特殊之处在于如果未进行指定则会默认搜索项目META-INF/spring.factories文件中注册的类但是它

搜索的注册类在spring.factories中的key是被ImportAutoConfiguration注解的类的全限

定名称。

显然这里的key为org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc。

以上功能也就解释了为什么在单元测试中更多的是使用ImportAutoConfiguration注解来进行自动配置了。

在spring-boot-test-autoconfigure项目的spring.factories文件中的相关配置如下

AutoConfigureMockMvc

org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvcorg.springframework.boot.test.autoconfigure.web.servlet.MockMvcAutoConfiguration,

org.springframework.boot.test.autoconfigure.web.servlet.MockMvcWebClientAutoConfiguration,

org.springframework.boot.test.autoconfigure.web.servlet.MockMvcWebDriverAutoConfiguration,

org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration,

org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerAutoConfiguration,

org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration,

org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration,

org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration,

org.springframework.boot.test.autoconfigure.web.servlet.MockMvcSecurityConfiguration

也就是说当使用ImportAutoConfiguration注解并未指定classes属性值时默认自动配置上述自动配置类。

使用AutoConfigureMockMvc注解会导入MockMvcAutoConfiguration自动配置类该类就是专门为MockMvc相关功能提供自动配置的。

false)

ConditionalOnWebApplication(type

Type.SERVLET)

AutoConfigureAfter(WebMvcAutoConfiguration.class)

EnableConfigurationProperties({

public

webMvcProperties;MockMvcAutoConfiguration(WebApplicationContext

context,

webMvcProperties;}....}注解部分说明MockMvcAutoConfiguration需要在Web应用程序类型为Servlet且在WebMvcAutoConfiguration自动配置之后进行自动配置。

另外通过EnableConfigurationProperties导入了ServerProperties和WebMvcProperties两个配置属性类并通过构造方法设置为成员变量。

4.3、使用方式

MockMvcBuilder构造MockMvc的构造器mockMvc调用perform执行一个RequestBuilder请求调用Controller的业务处理逻辑perform返回ResultActions返回操作结果通过ResultActions提供了统一的验证方式使用StatusResultMatchers对请求结果进行验证使用ContentResultMatchers对请求返回的内容进行验证

2、MockMvcBuilder

MockMvc是spring测试下的一个非常好用的类他们的初始化需要在setUp中进行。

MockMvcBuilder是用来构造MockMvc的构造器其主要有两个实现StandaloneMockMvcBuilder和DefaultMockMvcBuilder前者继承了后者。

MockMvcBuilders.webAppContextSetup(WebApplicationContext

context)指定WebApplicationContext将会从该上下文获取相应的控制器并得到相应的MockMvcMockMvcBuilders.standaloneSetup(Object...

controllers)通过参数指定一组控制器这样就不需要从上下文获取了比如this.mockMvc

MockMvcBuilders.standaloneSetup(this.controller).build();这些Builder还提供了其他api可以自行百度

从名字可以看出RequestBuilder用来构建请求的其提供了一个方法buildRequest(ServletContext

servletContext)用于构建MockHttpServletRequest其主要有两个子类MockHttpServletRequestBuilder和MockMultipartHttpServletRequestBuilder如文件上传使用即用来Mock客户端请求需要的所有数据。

常用API

urlVariables)根据uri模板和uri变量值得到一个GET请求方式的RequestBuilder如果在controller的方法中method选择的是RequestMethod.GET那在controllerTest中对应就要使用MockMvcRequestBuilders.getpost(String

urlTemplate,

urlVariables)同get类似但是是POST方法put(String

urlTemplate,

urlVariables)同get类似但是是PUT方法delete(String

urlTemplate,

同get类似但是是DELETE方法options(String

urlTemplate,

urlVariables)同get类似但是是OPTIONS方法

4、ResultActions

调用MockMvc.perform(RequestBuilder

requestBuilder)后将得到ResultActions对ResultActions有以下三种处理

ResultActions.andExpect添加执行完成后的断言。

添加ResultMatcher验证规则验证控制器执行完成后结果是否正确ResultActions.andDo添加一个结果处理器比如此处使用.andDo(MockMvcResultHandlers.print())输出整个响应结果信息可以在调试的时候使用ResultActions.andReturn表示执行完成后返回相应的结果

ResultHandler用于对处理的结果进行相应处理的比如输出整个请求/响应等信息方便调试Spring

mvc测试框架提供了MockMvcResultHandlers静态工厂方法该工厂提供了ResultHandler

print()返回一个输出MvcResult详细信息到控制台的ResultHandler实现。

ALL

ResultMatcher用来匹配执行完请求后的结果验证其就一个match(MvcResult

result)断言方法如果匹配失败将抛出相应的异常spring

mvc测试框架提供了很多***ResultMatchers来满足测试需求。

MockMvcResultMatchers类提供了许多静态方法提供了多种匹配器

request()返回RequestResultMatchers访问与请求相关的断言

asyncStarted断言异步处理开始asyncNotStarted断言异步不开始asyncResult断言使用给定匹配器进行异步处理的结果attribute用于断言请求属性值sessionAttribute用于断言Session会话属性值sessionAttributeDoesNotExist断言Session会话属性不存在

handler()返回HandlerResultMatchers对处理请求的处理程序的断言的访问

handlerType断言处理请求的处理程序的类型methodCall断言用于处理请求的控制器方法methodName断言用于处理请求的控制器方法的名称method断言用于处理请求的控制器方法

model()ModelResultMatchers访问与模型相关的断言

attribute断言一个模型属性值attributeExists断言一个模型属性存在attributeDoesNotExist断言一个模型属性不存在attributeErrorCount断言给定的模型属性有指定个数的错误attributeHasErrors断言给定的模型属性有错误attributeHasNoErrors断言给定的模型属性没有错误attributeHasFieldErrors断言给定的模型属性字段有错误attributeHasFieldErrorCode使用精确字符串匹配断言模型属性的字段错误代码errorCount断言模型中的错误总数hasErrors断言模型中有错误hasNoErrors断言模型中没有错误size断言模型属性的数量

view()返回ViewResultMatchers访问所选视图上的断言

name断言视图名

flash()返回FlashAttributeResultMatchers访问flash属性断言

attribute断言flash属性的值attributeExists断言给定的flash属性是否存在attributeCount断言flash属性的数量

String

expectedUrl)断言请求被转发到给定的URLforwardedUrlTemplate(String

urlTemplate,

uriVars)断言请求被转发到给定的URL模板forwardedUrlPattern(String

urlPattern)断言请求被转发到给定的URLredirectedUrl(String

expectedUrl)断言请求被重定向到给定的URLredirectedUrlTemplate(String

urlTemplate,

uriVars)断言请求被重定向到给定的URL模板redirectedUrlPattern(String

urlPattern)断言请求被重定向到给定的URLstatus()返回StatusResultMatchers访问响应状态断言

is断言响应状态码is1xxInformational断言响应状态码在1xx范围内is2xxSuccessful断言响应状态码在2xx范围内is3xxRedirection断言响应状态码在3xx范围内is4xxClientError断言响应状态码在4xx范围内is5xxServerError断言响应状态码在5xx范围内reason断言Servlet响应错误消息isContinue响应状态码是100isSwitchingProtocols响应状态码是101isProcessing响应状态码是102isCheckpoint响应状态码是103isOk响应状态码是200isCreated响应状态码是201isAccepted响应状态码是202isNonAuthoritativeInformation响应状态码是203isNoContent响应状态码是204isResetContent响应状态码是205isPartialContent响应状态码是206isMultiStatus响应状态码是207isAlreadyReported响应状态码是208isImUsed响应状态码是226isMultipleChoices响应状态码是300isMovedPermanently响应状态码是301isFound响应状态码是302isSeeOther响应状态码是303isNotModified响应状态码是304isUseProxy响应状态码是305isTemporaryRedirect响应状态码是307isPermanentRedirect响应状态码是308isBadRequest响应状态码是400isUnauthorized响应状态码是401isPaymentRequired响应状态码是402isForbidden响应状态码是403isNotFound响应状态码是404isMethodNotAllowed响应状态码是405isNotAcceptable响应状态码是406isProxyAuthenticationRequired响应状态码是407isRequestTimeout响应状态码是408isConflict响应状态码是409isGone响应状态码是410isLengthRequired响应状态码是411isPreconditionFailed响应状态码是412isPayloadTooLarge响应状态码是413isUriTooLong响应状态码是414isUnsupportedMediaType响应状态码是415isRequestedRangeNotSatisfiable响应状态码是416isExpectationFailed响应状态码是417isIAmATeapot响应状态码是418isInsufficientSpaceOnResource响应状态码是419isMethodFailure响应状态码是420isDestinationLocked响应状态码是421isUnprocessableEntity响应状态码是422isLocked响应状态码是423isFailedDependency响应状态码是424isTooEarly响应状态码是425isUpgradeRequired响应状态码是426isPreconditionRequired响应状态码是428isTooManyRequests响应状态码是429isRequestHeaderFieldsTooLarge响应状态码是431isUnavailableForLegalReasons响应状态码是451isInternalServerError响应状态码是500isNotImplemented响应状态码是501isBadGateway响应状态码是502isServiceUnavailable响应状态码是503isGatewayTimeout响应状态码是504isHttpVersionNotSupported响应状态码是505isVariantAlsoNegotiates响应状态码是506isInsufficientStorage响应状态码是507isLoopDetected响应状态码是508isBandwidthLimitExceeded响应状态码是509isNotExtended响应状态码是510isNetworkAuthenticationRequired响应状态码是511

header()返回HeaderResultMatchers访问响应头断言

string断言响应头的主值stringValues断言响应头的值exists断言指定的响应头存在doesNotExist断言指定的响应头不存在longValue将指定响应头断言为longdateValue断言指定响应头解析为日期

content()返回ContentResultMatchers访问响应体断言

contentType断言Content-Type给定的内容类型必须完全匹配包括类型、子类型和参数contentTypeCompatibleWith断言Content-Type与指定的类型兼容encoding断言响应的字符编码string断言响应体内容作为字符串bytes断言响应体内容作为字节数组xml断言响应体内容作为Xmlsource断言响应体内容作为Sourcejson断言响应体内容作为json

jsonPath(String

args)返回JsonPathResultMatchers使用JsonPath表达式访问响应体断言

prefix断言JSON有效负载是否添加了给定的前缀value根据JsonPath断言结果值exists根据JsonPath断言在给定路径上存在非空值doesNotExist根据JsonPath断言在给定路径上不存在非空值isEmpty根据JsonPath断言给定路径中存在空值isNotEmpty根据JsonPath断言给定路径中不存在空值hasJsonPath根据JsonPath断言给定路径中存在一个值doesNotHaveJsonPath根据JsonPath断言给定路径中不存在一个值isString根据JsonPath断言结果是StringisBoolean根据JsonPath断言结果是BooleanisNumber根据JsonPath断言结果是NumberisArray根据JsonPath断言结果是ArrayisMap根据JsonPath断言结果是Map

jsonPath(String

matcher)根据响应体计算给定的JsonPath表达式并使用给定的Hamcrest

expression,

targetType)根据响应体计算给定的JsonPath表达式并使用给定的Hamcrest

Matcher断言结果值在应用匹配器之前将结果值强制转换为给定的目标类型xpath(String

expression,

args)返回XpathResultMatchers使用XPath表达式访问响应体断言以检查响应体的特定子集

Matcher找到的Node内容nodeList计算XPath并断言与给定的Hamcrest

Matcher找到的NodeList内容exists计算XPath并断言内容存在doesNotExist计算XPath并断言内容不存在nodeCount计算XPath并断言使用给定的Hamcrest

Matcher找到的节点数string应用XPath并断言用给定的Hamcrest

Matcher找到的String值number计算XPath并断言用给定的Hamcrest

Matcher找到的Double值booleanValue计算XPath并断言找到的Boolean

xpath(String

args)使用XPath表达式访问响应体断言以检查响应体的特定子集cookie()返回CookieResultMatchers访问响应cookie断言

Matcher断言一个cookie值exists断言cookie存在doesNotExist断言cookie不存在maxAge使用Hamcrest

Matcher断言cookie的maxAgepath用Hamcrest

Matcher断言一个cookie的路径domain使用Hamcrest

Matcher断言cookie的域comment用Hamcrest

Matcher断言一个cookie的注释version用Hamcrest

Matcher断言一个cookie的版本secure断言cookie是否必须通过安全协议发送httpOnly断言cookie是否只能是HTTP

6、MvcResult

即执行完控制器后得到的整个结果并不仅仅是返回值其包含了测试时需要的所有信息。

MvcResult有两个实现类

DefaultMvcResult一个简单的默认实现PrintingMvcResult待打印功能的实现

常用方法

getRequest返回执行的请求getResponse返回结果响应getHandler返回已执行的处理程序getInterceptors返回处理程序周围的拦截器getModelAndView返回处理程序准备的ModelAndViewgetResolvedException返回由处理程序引发并通过HandlerExceptionResolver成功解决的任何异常getFlashMap返回在请求处理期间保存的FlashMapgetAsyncResult得到异步执行的结果

5、业务代码

http://mybatis.org/dtd/mybatis-3-mapper.dtd

mapper

resultTypepers.zhang.entity.UserSELECT

id,

resultTypepers.zhang.entity.UserSELECT

id,

userMapper.list();}Overridepublic

Integer

userMapper.add(user);}Overridepublic

Integer

userMapper.update(user);}Overridepublic

Integer

userMapper.deleteById...);return

userMapper.deleteById(id);}Overridepublic

User

userService;GetMapping(/list)public

ListUser

userService.list();}GetMapping(/info)public

User

UserService.getUserById);return

userService.getById(id);}PostMapping(/add)public

Integer

userService.add(user);}PostMapping(/update)public

Integer

userService.update(user);}PostMapping(/delete)public

Integer

在UserMapperTest测试类中可以直接使用Autowired来装配UserMapper这个Bean。

而且SpringBootTest注解会自动帮我们完成启动一个Spring容器ApplicationContext然后连接数据库执行一套完整的业务逻辑。

import

org.springframework.beans.factory.annotation.Autowired;

import

org.springframework.boot.test.context.SpringBootTest;

import

org.junit.jupiter.api.Assertions.*;

import

org.assertj.core.api.Assertions.*;SpringBootTest

class

userMapper.list();assertThat(list.size()).isEqualTo(3);assertThat(list).extracting(id,

name,

User();user.setName(zhangsan);user.setAge(30);user.setHeight(1.66);Integer

effectRows

userMapper.add(user);assertThat(effectRows).isEqualTo(1);}Testvoid

update()

User();user.setName(zhangsan);user.setAge(33);user.setHeight(1.88);user.setId(7L);Integer

effectRows

userMapper.update(user);assertThat(effectRows).isEqualTo(1);}Testvoid

deleteById()

userMapper.deleteById(7L);assertThat(effectRows).isEqualTo(1);}Testvoid

getById()

User();expect.setId(1L);expect.setName(tom);expect.setAge(18);expect.setHeight(1.77);User

user

userMapper.getById(1L);assertThat(user).isEqualTo(expect);}

}6.2、Service层测试

上面的测试代码是连接真实数据库来执行真实的Dao层数据库查询逻辑。

而在实际开发中有时候需要独立于数据库进行Service层逻辑的开发。

这个时候就可以直接把数据库Dao层代码Mock掉。

import

org.junit.jupiter.api.BeforeEach;

import

pers.zhang.mapper.UserMapper;import

java.util.ArrayList;

org.assertj.core.api.Assertions.*;

import

userMapper;//把Mock掉的Dao层注入ServiceInjectMocksprivate

UserServiceImpl

{//开启Mockito注解MockitoAnnotations.openMocks(this);}Testvoid

list()

1.83));//打桩when(userMapper.list()).thenReturn(users);//调用ListUser

list

userService.list();list.forEach(System.out::println);//验证verify(userMapper,

add()

1.80);//打桩when(userMapper.add(isA(User.class))).thenReturn(1);//调用Integer

effectRows

userService.add(user);assertThat(effectRows).isEqualTo(1);//验证verify(userMapper,

update()

1.80);//打桩when(userMapper.update(argThat(u

{return

null;}))).thenReturn(1);//调用Integer

effectRows

userService.update(user);assertThat(effectRows).isEqualTo(1);//验证verify(userMapper,

times(1)).update(user);}Testvoid

deleteById()

{//打桩when(userMapper.deleteById(anyLong())).thenReturn(1);//调用Integer

effectRows

userService.deleteById(999L);assertThat(effectRows).isEqualTo(1);//验证verify(userMapper,

times(1)).deleteById(999L);}Testvoid

getById()

1.92);//打桩when(userMapper.getById(1L)).thenReturn(user);//调用User

actual

userService.getById(1L);assertThat(actual).isInstanceOf(User.class);//验证verify(userMapper,

}输出

userMapper.deleteById...6.3、Controller层测试

spring-boot-starter-test提供了MockMvc对Controller测试功能的强大支持。

import

org.junit.jupiter.api.BeforeEach;

import

org.mockito.MockitoAnnotations;

import

org.springframework.http.MediaType;

import

org.springframework.test.web.servlet.MockMvc;

import

org.springframework.test.web.servlet.MvcResult;

import

org.springframework.test.web.servlet.request.MockMvcRequestBuilders;

import

org.springframework.test.web.servlet.setup.MockMvcBuilders;

import

pers.zhang.service.UserService;import

static

org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

import

org.springframework.test.web.servlet.result.MockMvcResultHandlers.*;class

UserControllerTest

{//开启Mockito注解MockitoAnnotations.openMocks(this);//初始化MockMvc将UserController注入其中mockMvc

MockMvcBuilders.standaloneSetup(userController).build();}Testvoid

list()

{//打桩when(userService.list()).thenReturn(Arrays.asList(new

User(1L,

mockMvc.perform(MockMvcRequestBuilders.get(/user/list).contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON)).andExpect(status().isOk()).andDo(print()).andReturn();System.out.println(mvcResult.getResponse().getContentAsString());//验证verify(userService,

getUserById()

1.77);when(userService.getById(anyLong())).thenReturn(user);//调用MvcResult

mvcResult

mockMvc.perform(MockMvcRequestBuilders.get(/user/info).accept(MediaType.APPLICATION_JSON).contentType(MediaType.APPLICATION_JSON).param(id,

1)).andExpect(status().isOk()).andDo(print()).andReturn();System.out.println(mvcResult.getResponse().getContentAsString());//验证verify(userService,

times(1)).getById(1L);}Testvoid

add()

User();user.setName(jerry);user.setAge(22);user.setHeight(1.74);//打桩when(userService.add(isA(User.class))).thenReturn(1);//调用MvcResult

mvcResult

mockMvc.perform(MockMvcRequestBuilders.post(/user/add).accept(MediaType.APPLICATION_JSON).contentType(MediaType.APPLICATION_JSON).content({name:

jerry,

1.74})).andExpect(status().isOk()).andDo(print()).andReturn();System.out.println(mvcResult.getResponse().getContentAsString());//验证verify(userService,

update()

1.77);//打桩when(userService.update(isA(User.class))).thenReturn(1);//调用MvcResult

mvcResult

mockMvc.perform(MockMvcRequestBuilders.post(/user/update).accept(MediaType.APPLICATION_JSON).contentType(MediaType.APPLICATION_JSON).content({id:

name:

1.77})).andExpect(status().isOk()).andDo(print()).andReturn();System.out.println(mvcResult.getResponse().getContentAsString());//验证verify(userService,

times(1)).update(user);}Testvoid

delete()

{//打桩when(userService.deleteById(anyLong())).thenReturn(1);//调用MvcResult

mvcResult

mockMvc.perform(MockMvcRequestBuilders.post(/user/delete).accept(MediaType.APPLICATION_JSON).param(id,

1)).andExpect(status().isOk()).andDo(print()).andReturn();System.out.println(mvcResult.getResponse().getContentAsString());//验证verify(userService,

}7、JSON接口测试

使用JsonPath可以像JavaScript语法一样方便地进行JSON数据返回的访问操作。

import

org.springframework.beans.factory.annotation.Autowired;

import

org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;

import

org.springframework.boot.test.context.SpringBootTest;

import

org.springframework.http.MediaType;

import

org.springframework.test.web.servlet.MockMvc;

import

org.springframework.test.web.servlet.request.MockMvcRequestBuilders;

import

org.springframework.test.web.servlet.result.MockMvcResultHandlers;

import

org.springframework.test.web.servlet.result.MockMvcResultMatchers;SpringBootTest

AutoConfigureMockMvc

{mockMvc.perform(MockMvcRequestBuilders.get(/user/list).accept(MediaType.APPLICATION_JSON))//响应码200.andExpect(MockMvcResultMatchers.status().isOk())//json数组长度为3.andExpect(MockMvcResultMatchers.jsonPath($.length(),

Matchers.equalTo(3)))//name包含指定值.andExpect(MockMvcResultMatchers.jsonPath($..name,

jerry,

mike))).andDo(MockMvcResultHandlers.print());}Testvoid

getUserById()

{mockMvc.perform(MockMvcRequestBuilders.get(/user/info).accept(MediaType.APPLICATION_JSON).param(id,

1)).andExpect(MockMvcResultMatchers.status().isOk()).andExpect(MockMvcResultMatchers.jsonPath($.name,

Matchers.equalTo(tom))).andExpect(MockMvcResultMatchers.jsonPath($.age,

Matchers.equalTo(18))).andExpect(MockMvcResultMatchers.jsonPath($.height,

Matchers.equalTo(1.77))).andDo(MockMvcResultHandlers.print());}Testvoid

add()

{mockMvc.perform(MockMvcRequestBuilders.post(/user/add).contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON).content({name:

zhangsan,

1.76})).andExpect(MockMvcResultMatchers.status().isOk()).andExpect(MockMvcResultMatchers.jsonPath($,

Matchers.equalTo(1))).andDo(MockMvcResultHandlers.print());}Testvoid

update()

{mockMvc.perform(MockMvcRequestBuilders.post(/user/update).contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON).content({id:

name:

1.76})).andExpect(MockMvcResultMatchers.status().isOk()).andExpect(MockMvcResultMatchers.jsonPath($,

Matchers.equalTo(1))).andDo(MockMvcResultHandlers.print());}Testvoid

delete()

{mockMvc.perform(MockMvcRequestBuilders.post(/user/delete).contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON).param(id,

9)).andExpect(MockMvcResultMatchers.status().isOk()).andExpect(MockMvcResultMatchers.jsonPath($,

Matchers.equalTo(1))).andDo(MockMvcResultHandlers.print());}



SEO优化服务概述

作为专业的SEO优化服务提供商,我们致力于通过科学、系统的搜索引擎优化策略,帮助企业在百度、Google等搜索引擎中获得更高的排名和流量。我们的服务涵盖网站结构优化、内容优化、技术SEO和链接建设等多个维度。

百度官方合作伙伴 白帽SEO技术 数据驱动优化 效果长期稳定

SEO优化核心服务

网站技术SEO

  • 网站结构优化 - 提升网站爬虫可访问性
  • 页面速度优化 - 缩短加载时间,提高用户体验
  • 移动端适配 - 确保移动设备友好性
  • HTTPS安全协议 - 提升网站安全性与信任度
  • 结构化数据标记 - 增强搜索结果显示效果

内容优化服务

  • 关键词研究与布局 - 精准定位目标关键词
  • 高质量内容创作 - 原创、专业、有价值的内容
  • Meta标签优化 - 提升点击率和相关性
  • 内容更新策略 - 保持网站内容新鲜度
  • 多媒体内容优化 - 图片、视频SEO优化

外链建设策略

  • 高质量外链获取 - 权威网站链接建设
  • 品牌提及监控 - 追踪品牌在线曝光
  • 行业目录提交 - 提升网站基础权威
  • 社交媒体整合 - 增强内容传播力
  • 链接质量分析 - 避免低质量链接风险

SEO服务方案对比

服务项目 基础套餐 标准套餐 高级定制
关键词优化数量 10-20个核心词 30-50个核心词+长尾词 80-150个全方位覆盖
内容优化 基础页面优化 全站内容优化+每月5篇原创 个性化内容策略+每月15篇原创
技术SEO 基本技术检查 全面技术优化+移动适配 深度技术重构+性能优化
外链建设 每月5-10条 每月20-30条高质量外链 每月50+条多渠道外链
数据报告 月度基础报告 双周详细报告+分析 每周深度报告+策略调整
效果保障 3-6个月见效 2-4个月见效 1-3个月快速见效

SEO优化实施流程

我们的SEO优化服务遵循科学严谨的流程,确保每一步都基于数据分析和行业最佳实践:

1

网站诊断分析

全面检测网站技术问题、内容质量、竞争对手情况,制定个性化优化方案。

2

关键词策略制定

基于用户搜索意图和商业目标,制定全面的关键词矩阵和布局策略。

3

技术优化实施

解决网站技术问题,优化网站结构,提升页面速度和移动端体验。

4

内容优化建设

创作高质量原创内容,优化现有页面,建立内容更新机制。

5

外链建设推广

获取高质量外部链接,建立品牌在线影响力,提升网站权威度。

6

数据监控调整

持续监控排名、流量和转化数据,根据效果调整优化策略。

SEO优化常见问题

SEO优化一般需要多长时间才能看到效果?
SEO是一个渐进的过程,通常需要3-6个月才能看到明显效果。具体时间取决于网站现状、竞争程度和优化强度。我们的标准套餐一般在2-4个月内开始显现效果,高级定制方案可能在1-3个月内就能看到初步成果。
你们使用白帽SEO技术还是黑帽技术?
我们始终坚持使用白帽SEO技术,遵循搜索引擎的官方指南。我们的优化策略注重长期效果和可持续性,绝不使用任何可能导致网站被惩罚的违规手段。作为百度官方合作伙伴,我们承诺提供安全、合规的SEO服务。
SEO优化后效果能持续多久?
通过我们的白帽SEO策略获得的排名和流量具有长期稳定性。一旦网站达到理想排名,只需适当的维护和更新,效果可以持续数年。我们提供优化后维护服务,确保您的网站长期保持竞争优势。
你们提供SEO优化效果保障吗?
我们提供基于数据的SEO效果承诺。根据服务套餐不同,我们承诺在约定时间内将核心关键词优化到指定排名位置,或实现约定的自然流量增长目标。所有承诺都会在服务合同中明确约定,并提供详细的KPI衡量标准。

SEO优化效果数据

基于我们服务的客户数据统计,平均优化效果如下:

+85%
自然搜索流量提升
+120%
关键词排名数量
+60%
网站转化率提升
3-6月
平均见效周期

行业案例 - 制造业

  • 优化前:日均自然流量120,核心词无排名
  • 优化6个月后:日均自然流量950,15个核心词首页排名
  • 效果提升:流量增长692%,询盘量增加320%

行业案例 - 电商

  • 优化前:月均自然订单50单,转化率1.2%
  • 优化4个月后:月均自然订单210单,转化率2.8%
  • 效果提升:订单增长320%,转化率提升133%

行业案例 - 教育

  • 优化前:月均咨询量35个,主要依赖付费广告
  • 优化5个月后:月均咨询量180个,自然流量占比65%
  • 效果提升:咨询量增长414%,营销成本降低57%

为什么选择我们的SEO服务

专业团队

  • 10年以上SEO经验专家带队
  • 百度、Google认证工程师
  • 内容创作、技术开发、数据分析多领域团队
  • 持续培训保持技术领先

数据驱动

  • 自主研发SEO分析工具
  • 实时排名监控系统
  • 竞争对手深度分析
  • 效果可视化报告

透明合作

  • 清晰的服务内容和价格
  • 定期进展汇报和沟通
  • 效果数据实时可查
  • 灵活的合同条款

我们的SEO服务理念

我们坚信,真正的SEO优化不仅仅是追求排名,而是通过提供优质内容、优化用户体验、建立网站权威,最终实现可持续的业务增长。我们的目标是与客户建立长期合作关系,共同成长。

提交需求或反馈

Demand feedback