• Spring Controller层测试 – 03 WebContext & MockMVC

    这种Controller层测试方案会(部分)加载Spring Application Context。不过仍然还是主要是用MockMVC来进行测试,也不需要部署WebServer。 示例代码如下: 和standalone MockMVC模式相比,这种测试方案主要有如下几点不同。 SpringRunner 这种测试是由SpringRunner来执行的。SpringRunner(部分)完成了Spring Context的初始化工作,在执行测试的时候,在日志开始的部分可以看到加载Context的内容。 MockMVC 自动化配置 使用@WebMVCTest注解可以完成MockMVC实例的自动化配置,以便于使用@Autowire注解来引用这个实例。同时,在@WebMVCTest注解中还指明了要测试的Controller类,这样Spring就会在Context中加载该Controller类及其相关的配置项。 此外,@WebMVCTest注解还会主动发现Controller类相关的Filter类和Controller Advice类并完成注入。这样,我们就不需要在setup()方法中再对其进行配置了。 使用MockBean 前面提到过这种测试方案是部分加载了Spring Context。原因就是@WebMVCTest在加载类的时候只会扫描含有@Controller,@ControllerAdvice, @JsonComponent,Filter,WebMvcConfigurer和HandlerMethodArgumentResolver这些注解或接口的类,也就是在三层模型中WEB层相关的类,而@Component注解的类则会被忽略掉。因此我们无法直接从Context获取IWorkerService实例。 因此测试中使用的WorkerService实例是通过@MockBean注解生成并注入到了Spring Context中的,并不是真正的WorkerService实例。 没有真正的服务调用 需要注意这里的返回值仍然是伪造的,在测试中也没有用到任何Web Server。 这次测试的主要关注点仍然是Controller类的内部逻辑,以及一些相关的角色如Filter和Controller Advice是如何影响Controller的返回值的。 总结 这次测试和使用MockMVC Standalone模式的主要区别在于不需要显式加载Controller相关的角色,因为这里使用到了Spring Context。参与测试的角色如下图: 如果我们创建了新的Filter、新的Controller Advice或者其他参与到WEB请求响应过程中的角色,也都会在测试中完成自动注入,不需要任何其它的配置。这和我们实际使用的场景非常类似。 这次测试可以算是向集成测试的一个小小过渡。这里我们没有做任何配置就实现了对Filter和Controller Advice的测试,如果有更多的其它角色,也可以直接集成到测试中。 Spring Controller测试 – 01 概述 Spring Controller测试 – 02 Standalone MockMVC Spring Controller测试 – 03 WebContext & MockMVC Spring Controller测试 – 04 SpringBootTest & MockMVC Spring Controller测试 – 05 SpringBootTest & WebServer 其他:示例代码可在CSDN下载,地址:https://download.csdn.net/download/tianxiexingyun/11065824

    [阅读更多...]
  • Spring Controller层测试 – 02 Standalone MockMVC

    在Spring中,可以在Standalone模式下使用MockMVC来进行服务内测试,此时我们不会加载任何Context。来看个例子: MockitoJUnitRunner和MockMVC 代码中使用了MockitoJUnitRunner来运行单元测试。这个类是由Mockito提供的,并在内置的JUnit Runner上添加了一些功能:比如检测框架运行,初始化所有用@Mock注解声明的变量,这样就不需要再调用Mockito.initMocks()方法来显式mock对象了了。 代码中使用@Mock注解mock了一个IWorkerService的实例。这个IWorkerService的实例需要注入到WorkerController类的实例中。所以我们在声明WorkerController实例的时候使用了@InjectMocks注解。这样,通过这个注解我们使用mock出的IWorkerService实例替换了真正的WorkerServiceImpl实例。 在每个测试中,我们使用MockMvc对象来执行各种各样的模拟请求(如GET、POST等请求),之后我们也会收到一个MockHttpServletResponse对象作为返回值。注意这个返回值也不是一个真正的返回值,也是模拟的。 JacksonTester初始化 代码中使用JacksonTester.initFields()方法创建了一个JacksonTester对象。JacksonTester是Spring提供的一个工具类,可以使用这个JacksonTester对象来生成Worker实例的json字符串。 配置standalone MockMVC实例 每个测试在执行前都会调用setup方法。在setup方法中我们采用Standalone模式创建了MockMVC实例,并在配置中添加了要进行测试的Controller实例,以及新建的Controller Advice实例和HTTP Filter实例。当然也可以用类成员的形式创建Controller Advice和HTTPFilter实例。现在我们可以看到这种测试方法的不足了:所有Controller Advice和Filter中的逻辑都需要在setup()方法中进行配置。这是因为我们这里没有任何Spring Context可以注入Advice和Filter。 使用MockMVC测试ControllerAdvice和Filter 在测试方法getWhenNotExists()中,我们测试了id不存在的情况下,请求返回的状态码是否是HttpStatus.NOT_FOUND。如果返回值与预期一致的话,说明ControllerAdvice是能够正常工作的。 在最后的测试方法workerFilter()中,我们测试了返回结果中有没有WorkerFilter添加的请求头信息。如果返回结果的header中有添加的请求头信息,说明Filter是能够正常工作的。 也可以做个验证,注释掉setup()方法中关于ControllerAdvice和Filter的配置,然后再运行getWhenNotExists()和workerFilter()方法。可以看到测试会执行失败,因为相关的ControllerAdvice和Filter无法注入到运行环境中。 另外,在这里进行ControllerAdvice和Filter的测试多少有些不妥,因为我们的目标是测试Controller层的执行逻辑。对这二者的测试可以留到集成测试中进行。 总结 使用MockMVC Standalone模式测试对Controller层进行测试不会使用任何Spring容器,Controller层所依赖的其他服务均是mock出来的,也就是说除了Controller层的逻辑,其他相关数据都是模拟的。描述如下图: 这种测试方法的优势是: 不依赖Spring容器,启动和测试耗时较少 集中于Controller层逻辑,不受其他服务接口的影响 当然其优势也是其不足所在,我们通常不会在Controller层写入太多的业务逻辑,而是将业务逻辑写在Service层。但是因为这种测试方案中没有加载Spring容器,所以无法注入依赖的Service类,因此就无法测试到Service中实现的业务逻辑。 Spring Controller测试 – 01 概述 Spring Controller测试 – 02 Standalone MockMVC Spring Controller测试 – 03 WebContext & MockMVC Spring Controller测试 – 04 SpringBootTest & MockMVC Spring Controller测试 – 05 SpringBootTest & WebServer 其他:示例代码可在CSDN下载,地址:https://download.csdn.net/download/tianxiexingyun/11065824

    [阅读更多...]
  • Spring Controller层测试 – 01 概述

    Spring Controller层(Web层或API层)的测试有多种方案。有人倾向于使用纯单元测试,有人则倾向于使用集成测试。 单元测试和集成测试 先来看一下单元测试和集成测试的概念。 单元测试 单元测试是对软件中的最小可验证单元进行检查和验证。比如对Java中的类和方法的测试。 测试原则: 尽可能保证测试用例相互独立(测试用例中不能直接调用其他类的方法,而应在测试用例中重写模拟方法); 此阶段一般由软件的开发人员来实施,用以检验所开发的代码功能是否符合自己的设计要求 单元测试的作用: 可以尽早的发现缺陷; 利于重构; 简化集成; 辅助文档; 辅助设计。 单元测试的不足: 每个测试单元至少需要3~5行代码,存在投入与产出的平衡。 集成测试 集成测试是根据集成测试计划,一边将模块或其他软件单位组合成越来越大的系统,一边运行该系统,以分析所组成的系统是否正确,各组成部分是否合拍。集成测试的策略主要有自顶向下和自底向上两种。 集成测试的作用: 目的是覆盖更多的执行路径; 发现单元之间接口的错误; 发现集成后的软件同软件需求不一致的地方。 不足在于: 关注的粒度较大,控制起来比较困难 比较来说单元测试更加重视过程,而集成测试更加重视结果。 Spring提供的Controller层测试方案 我们可以在不运行web server的情况下直接对Controller的逻辑进行测试。要执行这种测试,就需要mock服务器的行为,因此也有可能在测试中漏掉一些内容。不过无需担心,我们可以在集成测试中覆盖可能会漏掉的那部分内容。 当然也可以启动一个WebServer来执行Controller层的测试。这样就可以加载整个应用的Context,因此这种方案也可以被视为一种较重的方案。 在这里介绍三种Spring Controller层的测试方案: 在standalone模式下使用MockMVC方式; 结合SpringRunner使用MockMVC方式; 结合SpringBootTest和RestTemplate进行测试。 通常在执行单元测试的时候使用MockMVC方案,在进行集成测试的时候则建议使用RestTemplate方案。原因是使用MockMVC方式可以对Controller进行细粒度的断言,而RestTemplate则是能够使用Spring的WebApplicationContext(部分或全部)。 示例程序 为了便于说明,写了一个简单的SpringBoot Application来进行演示。 在应用中有如下几个类: WorkerController:WEB接口类; WorkerControllerAdvisor:Controller异常统一处理类,这里捕获了NonExistException并修改返回的status为404; WorkerFilter:过滤器类,在response中添加了header值X-CHOBIT-APP=chobit-header IWorkerService:服务接口类; WorkerServiceImpl:服务实现类,模拟了数据库操作。 示例代码可在CSDN下载,地址:https://download.csdn.net/download/tianxiexingyun/11065824 接下来分几篇文章来介绍几种测试方案。 Spring Controller测试 – 01 概述 Spring Controller测试 – 02 Standalone MockMVC Spring Controller测试 – 03 WebContext & MockMVC Spring Controller测试 – 04 SpringBootTest & MockMVC Spring Controller测试 – 05 SpringBootTest & WebServer

    [阅读更多...]
  • Spark生成CSV后BOM变EF BF BD

    工作中需要通过Spark以csv格式导出日志文件。实现功能不太复杂,但是在中文识别上遇到了些问题。 解决csv文件中的乱码最直接的思路就是添加BOM,这样Excel在打开Excel的时候就知道使用什么样的编码来解析这篇文档了。 先写了段测试代码验证了下: 这段代码会生成一个包含中文字符的csv文件。添加的BOM提示了生成的文件使用的是UTF-8编码。经测试使用Excel打开该文件可以识别中文字符。 将相同的逻辑写到Spark程序中,结果却没有预期的那么美好:中文乱码依然存在,并且新添加的BOM也变成了乱码。BOM变成了这几个奇怪又熟悉的字符: 最后面那个“潔”还吞了一个英文字符e。 在十六进制下查看BOM变成了这几个字符: 也就是说BOM中的三个字符每个都被替换成了EF BF BD。 查了下资料,这三个字符的意义是这样的: 它是很多编程语言以及库中的备胎,即无效的码点值在编码的时候会默认用这个码点值进行替换,即utf-8中的超级「备胎」(REPLACEMENT CHARACTER) 也就是说Spark将我添加的BOM字符串视为了无效字符。 为什么会这样呢?一时陷入了无效的思索和尝试中。 说下最后的解决方案吧,巨简单:在使用BOM数组创建字符串的时候指明使用UTF8编码。就是像下面这样: 会出现这个问题估计是因为服务器的默认文件编码不是UTF-8导致的。

    [阅读更多...]
  • 关于Scrum敏捷开发的一点想法

    公司中层最近开始推scrum敏捷开发,落实到行动上大概有这么几个方面: 定期迭代,小步快跑; 产品经理创建Backlog(需求列表); 团队进行需求评审; 开发团队组织会议,对需求进行细化,分解成一个个可以执行的具体任务(task); 开发团队每人主动认领当前开发周期内的task,要求认领的Task不能主要是自己熟悉的部分; 及时同步Task进度到Jira上; 每天下班前站会,总结今天的工作内容,说明明天的工作计划; 当前Sprint完成后,进行总结会议,每人预先准备好总结内容,轮流发言。 实际上类似的流程我们的团队一直在进行。不同的是第5条,对于这种形式我多少持有一些保留态度。 其实第5条这种形式的优势是显而易见的: 能够促进团队成员熟悉所有的产品线; 不会出现一人缺席,一块功能就无人对接的情况; 避免团队成员挑活儿; 团队成员也能在产品、技术等多个层面得到全面的收益。 缺点则相对没那么明显:对于一个产品的每个子工程来说,它的负责人是流动的,换言之就是没有具体的负责人。正常情况下这当然是OK的;出了些问题也没什么大不了,全员一起扛,群策群力也能解决掉。但是一些中间的情况却可能出现问题,影响有这么几个方面: 每个工程有多个人插手,可能会出现整体风格的混乱,也容易出现一些互相拖后腿的情况; 因为不对具体的工程负责,可能就不会深入了解工程的每个细节; 同样因为不对具体工程负责,在需要做一些优化时,也许不会很流畅的进行下去。 举个例子吧:某个团队开发成员在偶然的情况下,发现某个模块需要做些优化,这个优化的效果可能在当下并不明显,不过却需要耗费一定的时间和精力。比较好的情况是,这个人比较有责任感,把这个情况上报了并且能够让团队领导认可这个优化有执行下去的必要性,那么这个优化就有可能被加入到下一次的Sprint中;在下一个Sprint中,负责这个优化的人,碰巧同样认可这个任务,对这个任务的了解也比较深入;那好这个人开始进行工作了,在工作中他发现这个优化需要改动大量的现有代码,比较幸运的是这个工程中的整体风格是一致的,在梳理现有工程上他只需要化费比较少的时间,其他同事也非常配合地帮助他修改各自曾经负责处理过的部分,他的态度也非常的积极,在指定时间内完成了这项任务。这样这次优化算是完美的实现了。 这个例子我写得并不痛快。只是想通过这个例子说明在这种模式下进行开发可能会面临的一些问题,这种模式的展开最需要的就是一个完美的团队:团队成员需要积极负责,团队成员间的沟通成本不能不够低,团队的每个成员对于问题的认可需要保持一致,甚至团队的编码风格等细节也有具体的要求。可想而知,比较困难。

    [阅读更多...]
  • WordPress java操作库

    前段时间有些需求就撸了个wordpress java操作库,主要是依赖wordpress XMLRPC实现。简单介绍下使用方式。 引入dependency: WordPress实例是所有操作的基础。简单的WordPress实例创建方式如下: xmlRpcUrl:xmlRpc服务端地址,WordPress博客的地址通常为博客地址 + xmlrpc.php,如:https://www.zhyea.com/xmlrpc.php username和password:登录WordPress博客后台使用的用户名和密码 也可以通过WPConfig(即WordPress配置对象)来更精细化地创建WordPress实例。WPConfig实例构建方式如下: 构建中的几个参数如下: trustAll:如博客未启用https,可忽略;如已启用https,建议将之设置为true,否则需要导入证书文件后再进行操作; connectTimeout:连接超时时间,单位ms; readTimeout:响应超时时间,单位ms。 使用WPConfig实例来创建WordPress实例: 新增文章可以使用WordPress实例的newPost()方法,示例代码如下: 该方法的返回结果为postId,即文章ID。 关于postName和postTitle:postTitle指的是文章标题;postName指的则是文章别名,主要在文章的url路径中使用;通常建议将postName设置为英文字符。 setCategories设置的是文章分类,如设置的分类在博客中不存在,将会按提交的分类名称创建新的分类。 setTags设置的是文章标签,同样的,如标签在博客中不存在将会创建新的标签。 我只用到了写入文章的能力,所以就写这些好了。更多用法参考wp-client的文档好了。 #########

    [阅读更多...]
  • Kafka java.net.SocketTimeoutException

    手上有一个消费Kafka的服务,这个服务每隔一段时间使用SimpleConsumer从kafka集群获取数据。Kafka的版本是0.8.21。今天这个服务一直在报错:SocketTimeoutException。异常信息如下: 从异常信息上来看是网络连接超时。翻看了一下任务的运行日志发现是在连接Kafka集群的k3节点时出的错。 尝试telnet连接k3节点的broker是可以的。这样子还报错就比较奇怪了。 使用glances查看了一下消费节点的本地运行环境,发现各项指标也都挺正常的。 登录到k3节点,查看kafka的运行日志也能够看到消费节点的连接记录: 查看了一下使用high-level consumer的其他服务也是正常的。 百思不得其解。但是问题还得解决,随手查看了一下k3节点的句柄数,发现一个进程的句柄数高得有点儿离谱哎: k3节点每个进程可以打开的最大句柄数: 53466就是k3节点上的kafka broker进程: 此时有必要看下这个进程的网络连接了: 发现有大量的CLOSE_WAIT和TIME_WAIT连接: 统计了下CLOSE_WAIT和TIME_WAIT连接的数量,好像有点儿多: 可以看出来所有连接中CLOSE_WAIT的记录占了绝大部分,和CLOSE_WAIT相关的连接又多是在j0节点上。 到j0节点查找和端口51436相关的进程,发现是一个kafka-manager服务。不是生产服务,直接关掉了事。 回到k3节点,重启该节点的broker。再进行测试,异常消失。持续观察几天,kafka的连接数是正常的,说明问题已经得到修复。 ##########

    [阅读更多...]
  • sbt下载加速方案

    本来不太想使用sbt,但是公司这边普遍要求使用sbt来进行部署。所以,so! sbt的语法什么的还好,唯一让人无法忍受的是sbt下载依赖的速度 —— 在国内大环境下实在是慢到了让人抓狂的程度。 要提升sbt下载依赖的速度,方法无非是那么几个:土豪可以考虑使用VPN,不想花钱可以考虑替换repository。 下面贴一下我用的repositories,并简单说明下。 第一行的local意思是使用本地源,不过好像没有什么大用。 第二行用的是公司的私有maven源。 第三行使用的是maven本机仓库,确定需要指明下仓库的位置才能生效。 第四行和第五行用的是社区公服。社区公服是一个公益项目,详情可以看下这里。社区公服原名广谈公服,主要能力就是解决sbt下载依赖太慢的问题。事实证明,社区公服真的是一个好东西,用上了以后,sbt下载依赖的速度当真飞快。 第六行是阿里云的依赖仓库,不过因为广谈公服排在前面,少见它能发挥作用。 第七行和第八行的仓库主要是解决typesafe相关源下载太慢或者下载失败的问题。即使用上了社区公服和阿里云maven仓库,一些typesafe出的sbt插件下载速度仍是巨慢。当时因为这个被绊住了好久。使用dl.bintray.com的源能很好地解决这个问题。据说typesafe的下载源也是指向这里,至于为什么typesafe源下载慢dl.bintray.com下载快就不清楚了。 有这几个源差不多就够了,添加的源如果太多,遇到一些特别“稀有”的依赖,sbt会串行地尝试每个源,如果每个源都失败,耗费的时间加在一起也很恐怖了。 ———————————————————————————— 现在又调整了一下repositories,主要添加了几个maven仓库的ivy布局兼容: 扯一些没用的。 编译工具我有用过maven、gradle和sbt三种。从私心上来说比较喜欢gradle的简洁特征的,sbt稍微有些复杂了,不过也比maven好一些。在IDE的支持上,maven因为用户基数大,得到的支持是最好的,gradle有安卓开发者加持也还不错,比较起来sbt是最差的:在idea上使用sbt经常出现依赖下载不了,索引失效等问题。所以在我看来,在国内进行scala开发最好选择gradle,其次选择maven,至于sbt——人生短暂,干嘛不做一些更有意义的事情呢。 关于sbt的的不足,这里有一篇文章《So, what’s wrong with SBT?》说了些干货,有兴趣可以看看,不过我是不太能完全理解的。

    [阅读更多...]
  • Java 中文字符按Unicode排序

    遇到了一个对包含中文的字符串进行排序的问题。要求按unicode编码对字符串进行排序。 测试字符串数组如下: 按unicode排序的期望结果应该是这样的: 先按java.lang.String类提供的默认比较方案进行实现,大致如下: 结果如下: 可以看到中文字符不能按照拼音进行排序。这时最直接的思路就是将中文字符转为拼音后再进行排序。但是要注意下,在这里面有个字符串不包含中文字符,这就容易导致顺序混乱。 如下面这几个字符串按拼音进行排序顺序如下: 可以看到字符串“1-qt”的位置出错了。 但是按拼音来说它的位置又是对的。这不能不说是一个让人有些头疼的地方。 不过不用担心,java提供了java.text.Collator类来支持规范化的字符串比较。 使用Collator来改造之前的代码: 改造后的程序执行排序的结果如下: 结果看着好像还OK。但是停停、注意下、字符串“1结束”的位置好像比较奇妙,理想情况下它应该在“1-营销”的后面。 这里出问题的原因我没有弄清楚。猜测着应该是java在Chinese语法中将中划线处理为空字符了。不过最根本的问题还是java对Unicode Collation Algorithm(UCA,Unicode整理算法)的支持并不好。 此时可以考虑使用IBM ICU提供的Collator来替换jdk默认的Collator。代码如下: 相关的依赖为: 执行结果为: 可以看到是和预期一致的。 参考文档 Java Unicode strings sorting ICU User Guide – JNI ##########

    [阅读更多...]
  • Kafka报错:Error reading field ‘topics’

    在kafka的server.log中发现了如下报错信息: 在StackOverflow上找到了类似的问题。知道报这个错的是因为kafka服务端的版本和kafka客户端的版本不一致导致的。 解决方案很简单: 调整kafka server端版本; 调整kafka客户端版本。 #######

    [阅读更多...]