前后端分离开发模式下后端质量的保证 —— 单元测试

概述

  在今天, 前后端分离已经是首选的一个开发模式。这对于后端团队来说其实是一个好消息,减轻任务并且更专注。在测试方面,就更加依赖于单元测试对于API以及后端业务逻辑的较验。当然单元测试并非在前后端分离流行之后才有,它很早就存在,只是鲜有人重视且真的能够用好它。而在前后端分离开发模式下,特别是两者交付时间差别很大的情况时,后端可能需要更加地依赖于单元测试来保证代码的正确性。

  本文主要围绕单元测试展开,从单元测试的基础概念说起,对比单元测试和集成测试,同时我们还会聊一聊单元测试与测试驱动开发的区别。在我们了解完单元测试的概念之后,我们会探讨一下什么样的单元测试算得上是好的单元测试,它们具备哪些特征,如何使用隔离框架来帮助我们对一些复杂的组件进行测试。最后一个内容也是本文想要阐述的重点: 单元测试是开发人员写的,那么开发人员在写自己的代码的时候,如何提高自己代码的可测试性? 什么样的代码算的上是对单元测试友好的代码? 带着这些问题,我们这就来开始我们的单元测试之旅。

什么是单元测试?

  有人可能写过单元测试,但是却不知道为什么要写单元测试,有人知道为什么要写单元测试,但不确定如何写才是好的单元测试。但是对于“测试” 我们每个人都轻车熟路, 你看看下面的功能是否似曾相识?

单元测试与测试

  测试种类分为很多种:单元测试、集成测试、系统测试、压力测试、负载测试、验收测试等等 ,我们今天不打算也不能进行系统性的介绍。作为开发人员,我们平常所说的“测试”。也就是说你代码写完了,老大问你测试通过了吗?你说过了,然后就可以Check in 代码了。这里的“测试”,实际上指的是不完整的功能测试。为什么说它不完整,是因为从专业测试的角度来讲,还需要定义规范的测试用例,用例写完之后还要开发和测试人员一起评审等等 。 而我们只是在脑海中预想了一下它应该如何工作的,应该给我什么结果等,然后运行一下,咦,还真是这样的,那我们的测试就算通过了。 会有多少Bug,就取决于我们这个预想有多细了,往往有时候我们只能想到很少一部份,这时候专业独立的测试人员就派上用场了。同时精通开发和测试的人是很有优势的,自己能够保证写出来的软件的质量,这也是现代敏捷开发团队所追求的,但是这样的人总是少之又少。

  单元测试是通过把一个应用程序拆分成可测试的足够小的部分,然后把每一部分与其它所有功能隔离开,单独对这一部分进行测试。而这个“可测试的足够小的部分”就称之为“单元“,在C语言中一个单元可以是一个函数,在C#中单元测试可以是一个类。 如果所有的单元都能够像我们所预料的正常工作,那么把他们合并起来就能够保证至少不会出现很严重的错误。

单元测试与集成测试 

   为什么要把这两项拿出来对比,是因为这两项很容易混淆,一不小心你就可能把单元测试写成集成测试了,这也是为什么单元测试有时候看起来那么糟糕的主要原因。我们上面说单元测试是把每一个单元孤立出来,在测试的时候不能和任何其它的单元有任何联系,这是单元测试,反过来你一旦在你的测试代码中引入了另外一个单元,那你就要开始小心,你是不是已经开始写集成测试了。 当然有时候往往不是引入了其它的一些单元,有可能是一些组件,下面列出了一些单元测试和集成测试的主要特点,希望能够帮助大家区分单元测试与集成测试。

单元测试

  • 可重复运行的

  • 持续长期有效,并且返回一致的结果

  • 在内存中运行,没有外部依赖组件(比如说真实的数据库,真实的文件存储等)

  • 快速返回结果

  • 一个测试方法只测试一个问题

集成测试

  • 利用真实的外部依赖(采用真实的数据库,外部的Web Service,文件存储系统等)

  • 在一个测试里面可能会多个问题(数据库正常确,配置,系统逻辑等)

  • 可以在运行较长时间之后才返回测试结果

单元测试与测试驱动开发(TDD)

  测试驱动开发其实我们用一个问题就可以解释清楚,那就是“你什么时候写单元测试?” 有人选择在开发的代码写完之后再写,这样我们的开发过程是: 理解需求-》编写代码-》针对代码结合需求写单元测试。后来大家发现,往往在写单元测试的时候发现自己有些需求没有理解清楚,或者这些需求原来设计的时候就没有考虑到,所以又重新改原来的代码。 于是有人就说,为什么我们不干脆反过来? 先写单元测试,再写代码?  所以我们开发的过程就变成了这样:理解需求-》针对需求写单元测试 -》 编写代码让单元测试通过。 最开始是叫测试先行(TFD: Test First Development) ,后来就发展成我们熟知的"测试驱动开发"了。

  测试驱动开发最大的好处是,让开发人员更好的理解需求,甚至是挖掘需求之后再进行开发。 当然,我们不可能一次性把所有的测试代码都写出来之后再写代码,这是一个重复迭代的过程:

  由于TDD不是我们本篇的主要内容,这里仅仅希望能给大家一个对TDD的浅显认识的同时了解到TDD与单元测试的联系。到这里,我们对于单元测试的概念就介绍的差不多了,接下来是代码时间。:) 我们来上一个真实的例子更形象的了解一下单元测试。

一个单元测试的例子

  那么问题来了,我们用什么来案例来写了一个单元测试的例子呢?既然这样,那么我们就用前两篇我们在领域模型驱动设计中讲到的用户注册的例子吧。在用户的领域服务中,UserService提供了一个Register的方法,通过用户名、邮箱和密码三个参数来创建一个用户的对象。 像所有注册逻辑一样,邮箱是不能重复的,这是我们现在这个领域服务中比较重要的业务逻辑,所以我们的单元测试必须要覆盖到。 我们的测试


namespace RepositoryAndEf.Domain.Tests

{

    public class UserServiceTests

    {

        private IRepository<User> _userRepository = new MockRepository<User>();


        [Fact]

        public void RegisterUser_ExpectedParameters_Success()

        {

            var userService = new UserService(_userRepository);

            var registeredUser = userService.Register(

                "hellojesseliu@outlook.com",

                "Jesse",

                "Jesse");


            var userFromRepository = _userRepository.GetById(registeredUser.Id);


            userFromRepository.Should().NotBe(null);

            userFromRepository.Email.Should().Be("hellojesseliu@outlook.com");

            userFromRepository.Name.Should().Be("Jesse");

            userFromRepository.Password.Should().Be("Jesse");

        }


        [Fact]

        public void RegisterUser_ExistedEmail_ThrowException()

        {

            var userService = new UserService(_userRepository);

            var registeredUser = userService.Register(

                "hellojesseliu@outlook.com",

                "Jesse",

                "Jesse");


            var userFromRepository = _userRepository.GetById(registeredUser.Id);

            userFromRepository.Should().NotBe(null);


            Action action = () => userService.Register(

                "hellojesseliu@outlook.com",

                "Jesse_01",

                "Jesse");

            action.ShouldThrow<ArgumentException>();

        }


        public void RegisterUser_ExistedName_ThrowException()

        {

            var userService = new UserService(_userRepository);

            var registeredUser = userService.Register(

                "hellojesseliu@outlook.com",

                "Jesse",

                "Jesse");


            var userFromRepository = _userRepository.GetById(registeredUser.Id);

            userFromRepository.Should().NotBe(null);


            Action action = () => userService.Register(

                "hellojesseliu_02@outlook.com",

                "Jesse",

                "Jesse");

            action.ShouldThrow<ArgumentException>();

        }


    }

}

   在这个例子中我们用到了 Fluentassertions、XUnit这两个开源组件。另外Moq作为一个不错的单元测试Mock框架也推荐给大家。

  • Fluentassertions:相对于.NET测试工具本身提供的Assert,Fluentassertions提供基于链式构建的一些更人性、易懂的方法来帮助写出更好理解的单元测试代码 。 上面代码中我们所用到的ShoudBe、NotBe、以及ShoudThrow等方法即来自于Fluentassertions,还有更多方法可以到官方文档上查询。

  • Xunit:这是一个开源的单元测试工具

  • Moq:为了让单元测试可以完全脱离外部组件,我们需要用到一些Mock对象和Stub对象,而Moq是一个开源的Mock类框架可以帮助我们实现这些功能 。我们上面代码中用到的MockRepository是我们自己用List封装的一个IRepository实例,支持增删改查,相当于我们把数据持久化于内存中。

namespace RepositoryAndEf.Data

{

    public class MockRepository<T> : IRepository<T> where T : BaseEntity

    {

        private List<T> _list = new List<T>();


        public T GetById(Guid id)

        {

            return _list.FirstOrDefault(e => e.Id == id);

        }


        public IEnumerable<T> Get(Expression<Func<T, bool>> predicate)

        {

            return _list.Where(predicate.Compile());

        }


        public bool Insert(T entity)

        {

            if (GetById(entity.Id) != null)

            {

                throw new InvalidCastException("The id has already existed");

            }


            _list.Add(entity);

            return true;

        }


        public bool Update(T entity)

        {

            var existingEntity = GetById(entity.Id);

            if (existingEntity == null)

            {

                throw new InvalidCastException("Cannot find the entity.");

            }


            existingEntity = entity;

            return true;

        }


        public bool Delete(T entity)

        {

            var existingEntity = GetById(entity.Id);

            if (existingEntity == null)

            {

                throw new InvalidCastException("Cannot find the entity.");

            }


            _list.Remove(entity);

            return true;

        }

    }

}


MockRepository.cs

   我们也可以用Moq框架在单元测试中临时初始化一个MockRepository

private readonly IRepository<User> _userRepository;

        private List<User> _userList = new List<User>();

        public UserServiceTests()

        {

            var mockRepository = new Mock<IRepository<User>>();


            // 初始化新增方法 

            mockRepository.Setup(r => r.Insert(It.IsAny<User>())).Returns((User user) =>

            {

                if (_userList.Any(u => u.Id == user.Id))

                {

                    throw new InvalidCastException("The id has already existed");

                }


                _userList.Add(user);

                return true;

            });


            _userRepository = mockRepository.Object;

        }

  在单元测试代码中临时初始化Mock repository

  • 更灵活:可以只初始化用到的方法 

  • 更强的控制能力:可以从外部(单元测试代码内)定义所有的行为 

  • 多态性:与其它单元测试类隔离,可以有不同的行为

Mock和Stub的区别

  因为有很多测试框架把Mock和Stub区别对待,初学者也会对这两个概念表示含糊不清。简单的来说,Mock与 Stub最大的区别是:

  Stub主要用来隔离其它的组件让单元测试可以正常的进行,我们不会对Stub来进行Assert。

       

  Mock则用来和测试代码进行交互,可以说我们会针对Mock来写测试代码,也会对它进行 Assert来验证我们的代码。

  在我们上面的代码中,我们只用到了一个Mock(MockRepository),如果同样是用户注册的业务,有哪些地方是我们可能需要用到Stub的? 试想一下现实的注册场景,如果用户注册成功了, 我们是不是需要给用户发送注册成功的邮件通知?这里有一点需要注意的是,注册用户相关的代码属于我们领域服务的职责,但是注册成功发送邮件、发送短信、甚至你要干一些系统相关的初始化操作都是属于应用层的事情。关于这点,大家还可以回顾之前的两篇关于DDD的文章。如果我们针对应用层的代码编写单元测试,那么我们就需要把一些组件比如邮件、日志等用Stub隔离掉,来保证测试代码的运行。


怎样才算好的单元测试?

什么是一个好的单元测试?

  • 是自动化的和可重复运行的

  • 很容易实现

  • 持续有用

  • 任何人只要轻松的点一下按钮就可以运行

  • 运行不会花太长的时间

  • 一直返回同样的结果(如果你不改变任何代码或参数)

  • 单元测试是完全隔离的,不应该有任何其它的依赖

  • 当单元测试失败的时候,应该一眼就看出是因为什么原因导致的这个失败

  • 一个测试方法只验证一个case,只用一个Mock,Stub可以是多个

  • 好的命名,最好是可以从方法名看出以下三个要素(所以一般我们采用三段命名法):

    • 测试目标

    • 条件 

    • 应该得到的结果

想知道你写的单元测试是不是好的单元测试么?

  • 2个星期,或者2个月甚至2年前写的单元测试还能运行并且得到同样的结果么?

  • 团队中的其它人也可以运行你2个月前写的单元测试么?

  • 可以点击一下按钮就运行你所有的单元测试,并返回正确的结果么?

  • 所有的单元测试可以在几分钟之内完成么?

    

测试用例都有哪些?

  写单元测试的代码可能是开发的好几倍,这句话是真的!在于你的单元测试用例覆盖的有多广,比如说我们上面针对用户注册这一个业务场景写了3个测试用例,其实是远远不够的。

非预期的用例

  不管我们上面那个完全成功注册的用例,还是另外两个由于邮箱和名称重复而没有注册成功的用例。这三个用户都是预期的,如果是非预期的,比如:

  • 如果邮箱地址不是一个正确格式的邮箱?

  • 如果我邮箱不填?用户名不填?

边界测试

  • 如果我的邮箱名称或者用户名长度超过最大限制?

回归测试

  修改bug是一件难过的事情,在复杂且耦合度很高的系统下修改bug是一件难过且胆破心惊的事情,那么你感受一下:在复杂且耦合度很高的系统下不断的修改同一个bug会是一种什么样的心情。我们后期维护代码的时候对于新增的改动也需要加上对应的测试代码来保证单元测试的完整性。

自动化——持续集成

  持续集成里面已经包含了单元测试的自动化。它倡导团队开发成员必须经常集成他们的工作,甚至每天都可能发生多次集成。而每次的集成都是通过自动化的构建来验证,包括自动编译、发布和测试,从而尽快地发现集成错误,让团队能够更快的开发内聚的软件。感兴趣的同学可以自行了解,这是一个关于DevOps的话题,就不在本文作过多的表述。光想象一下那种不管谁有代码check in都引发所有单元测试代码的自动运行,在单元测试覆盖的全的情况下基本可以过滤掉很多的潜在bug。 


提高代码的可测试性

  我们多数遇到的项目之所有很少看到单元测试的代码大概是因为以下的几个原因:

  • 领导不重视 ,团队内没有这个风气

  • 项目太紧,根本不给时间(可能也有领导不重视的原因)

  • 开发人员对于单元测试不熟悉 ,不知道怎么样写好单测试。(不好的单元测试代码,写了可能等于白写,因为根本没人去运行它们)

  • 解决方案里面的业务层根本没有办法写单元测试(耦合度太高,重依赖,这是当我排除前面3个困难之后,常常遇到的最后一道坎)

  关于最后一点是需要架构师、或者比较有经验在开发者在最开始设计系统结构的时候需要考虑到的。如果最开始没有考虑到怎么办? 那太好了,因为很多项目最开始都没有考虑到,所以我们的单元测试代码总是盛行不起来。(可怜这一层面的架构师也是少之又少,倒是有很多架构师活跃于各大论坛讲高并发、各种分布式组件,能挽起袖子去重构/优化代码结构的人真的少之又少。因为实在太累,而且搞不好还容易出错,属于最有挑战,但其实却往往不被老板重视的一项苦差事)遇到比较多的问题(包括BAT级别的项目,可能外面的架子、整体架构图画出来那是非常的漂亮,但是一旦涉及到业务层面的代码....后面我就不说了。)

整体架构层面的考虑

  如果我们现在是重新开始搭建一套系统,那我们可以怎样开始?或者说如果我们有魄力和决心去重构一套系统,我们该往哪些方向去走?—— 从DDD的分层架构说起

    分层: 首先是通过分层把业务与其它基础组件隔离开,不要让一些发邮件、记日志、写文件等这些基础组件混合了我们的业务,在应用层将领域业务与这些为应用服务的基础功能组合起来。在之前的一篇文章 《初探领域驱动设计——为复杂业务而生》有具体的介绍。

    

  领域业务层无依赖

  在洋葱架构中,核心(Core)层是与领域或技术无关的基础构件块,它包含了一些通用的构件块,例如list、case类或Actor等等。核心层不包含任何技术层面的概念,例如REST或数据库等等。 

  

  如果有依赖,请依赖于接口抽象,而非具体的实现,比如我们例子中的IRepository。这些架构思想其实已经很老很老了,但是我们多数的项目还停留在更更老的三层架构思想上,说好的技术极客们都去哪里了?

保持类的引用/依赖关系清晰,可注入

  不要使用静态方案

  且不要说一些面向对象的特性没有办法使用到,一旦开了这个口子。天知道你的代码里面会依赖于多少个外部静态方法,并且完全没有办法在测试代码中将它们mock掉,万一你在静态方法里面又有其它依赖,那对于单元测试来说就是一场终结。

  保持一个类所有的外部引用易见

  1.  所有外部引用易见
  2.  外部引用可注入/替换

  

  除了构造函数注入以外,我们还可以采用构造函数注入、字段、以及方法注入的方式,将我们的方法替换掉。这种方式不仅仅是对单元测试友好,更是一种良好的代码组织方式,是可能提供代码的易读性,以及可维护性的。要知道代码主要是给人阅读的,只是偶尔让机器执行一下。如果有跳槽经验的同学应该都有过那种到了一个公司,有一个很复杂的系统,但是没有任何的文档(稍微好一点的可能会有表字典)的感受,唯一了解系统业务的方式是play with the system 然后,看代码。 对于种无法一眼看到各个类之间的关系的代码,特别是一个类里面有好几百个方法、上万行代码的时候, 虽然我对于干这种事情已经轻车熟路,但当时的心情难免还是有些激(操)动(蛋)的。

 依赖于接口/抽象,而非实现 

  这点我想也就不需要细述了,在单元测试这个场景里面。我们主要是将业务与非业务相关功能用接口隔离开,那么我们在单元测试中就可以很灵活的用Mock或者Stub来替换。比如:读写文件、访问数据库、远程请求等等。

最后

   编写单元测试虽然简单,但是考验的却是细心和对业务的理解程度。而且往往写单元测试代码所花的时间比写功能代码还要多,在任务时间进度紧、又不受重视的情况下,自己很少有人会主动愿意去写。但是,好的单元测试代码确实在长期能够体现出它的价值。


原文地址:http://www.cnblogs.com/jesse2013/p/magic-of-unittesting.html


.NET社区新闻,深度好文,微信中搜索dotNET跨平台或扫描二维码关注

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/328743.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

查询近12个月的数据,没有数据的月份补零

可以有两种方法实现&#xff0c;一种是纯sql的方式&#xff0c;一种是sql和代码共用的形式 一、纯sql的形式 前半部分用于一个近12个月的表&#xff0c;然后再将查出来的数据和月份表相结合&#xff0c;就是想要的结果 select v.month,b.price price,ifnull(b.count,0) cou…

java图形验证码生成工具类

转载自 java图形验证码生成工具类生成验证码效果 ValidateCode.java 验证码生成类 package cn.dsna.util.images; import java.awt.Color; import java.awt.Font; import java.awt.Graphics2D; import java.awt.image.BufferedImage; import java.io.FileOutput…

.NET Core中的认证管理解析

0x00 问题来源 在新建.NET Core的Web项目时选择“使用个人用户账户”就可以创建一个带有用户和权限管理的项目&#xff0c;已经准备好了用户注册、登录等很多页面&#xff0c;也可以使用AuthorizeAttribute进行各种权限管理&#xff0c;看起来似乎十分方便。不过生成的代码都替…

散列算法,Remal使用散列算法

一、散列算法 散列算法让其保证不可逆&#xff0c;安全。这里举一个例子sh1的摘要算法。上代码 /*** 散列算法* author Administrator*/ public class HashRsaUtil {/*** 加密方式*/public static final String SHA1"SHA-1";/*** 加密次数*/public static final In…

简化得最没道理的6个汉字,让人大跌眼镜

文章来源于网络&#xff0c;侵删&#xff01;&#xff01;&#xff01; 1、“進”被简化为“进”&#xff0c;“進”字是让人“越走越佳”。 简化字却把它改成了“进”字&#xff0c;让你越走越走到“井”里去了&#xff0c;井底之蛙自生自灭。 2、“廠”被简化为“厂”字&…

c++

#include <iostream> #include<cmath> using namespace std; int main() { int x; double a; double b; while(cin >> x ){//注意while处理多个case for(int i 0 ;i < x ; i ){ cin>>a; …

在docker中运行ASP.NET Core Web API应用程序

本文是一篇指导快速演练的文章&#xff0c;将介绍在docker中运行一个ASP.NET Core Web API应用程序的基本步骤&#xff0c;在介绍的过程中&#xff0c;也会对docker的使用进行一些简单的描述。对于.NET Core以及docker的基本概念&#xff0c;网上已经有很多文章对其进行介绍了&…

谈谈 Java 的克隆

转载自 谈谈 Java 的克隆为什么要克隆对象 做开发很少用到克隆的。我能想得到的是用于调用方法时作为参数传递&#xff0c;为了保证方法调用前后对象的内部结构不被破坏&#xff0c;可以克隆一个对象作为参数传递。 使类具有克隆能力 有人可能注意到 Object 类中有一个 native…

android启调支付宝

网上找了一个可以起吊支付宝的appdemo &#xff0c;它集成了服务器端&#xff0c;我先将其分离为app和服务器端&#xff0c;保证app在接收参数后可以启调支付宝 &#xff08;保证app这边是正确的 不然出错都不知道是服务器出错还是app出错&#xff09;&#xff0c;在 找网上资…

shiro-身份授权流程、案例

一、身份授权流程 首先调用Subject.isPermitted/hasRole接口&#xff0c;委托给SecurityManager.SecurityManager接着会委托给内部组件Authorizer.Authorizer再将其请求委托给我们的Realm去做&#xff0c;Realm才是真正干活的.realm将用户请求的参数封装成权限对象&#xff0c…

对Java的URL类支持的协议进行扩展的方法

转载自 对Java的URL类支持的协议进行扩展的方法JAVA默认提供了对file,ftp,gopher,http,https,jar,mailto,netdoc协议的支持。当我们要利用这些协议来创建应用时&#xff0c;主要会涉及到如下几个类&#xff1a;java.net.URL、java.net.URLConnection、InputStream。URL类默认…

在.Net项目中使用Redis作为缓存服务

最近由于项目需要&#xff0c;在系统缓存服务部分上了redis&#xff0c;终于有机会在实际开发中玩一下&#xff0c;之前都是自己随便看看写写&#xff0c;很零碎也没沉淀下来什么&#xff0c;这次算是一个系统学习和实践过程的总结。 和Redis有关的基础知识 Redis是一个开源的分…

中国的程序员培训是不是有问题?

内容来源于&#xff0c;看最下面的出处&#xff0c;侵删 中国技术开放日的出海团对日本进行了为期一周的访问。笔者随行了头两天&#xff0c;参加Slush Asia大会&#xff0c;并访问了Gungho和Deloitte两家企业。虽然已经在日本生活了四年&#xff0c;但这样的体验却甚少&#x…

后台回调支付宝

https://blog.csdn.net/u012552275/article/details/78320051 网上找了一个可以起吊支付宝的appdemo &#xff0c;它集成了服务器端&#xff0c;我先将其分离为app和服务器端&#xff0c;保证app在接收参数后可以启调支付宝 &#xff08;保证app这边是正确的 不然出错都不知道…

解决高版本SpringBoot整合swagger时启动报错:Failed to start bean ‘documentationPluginsBootstrapper‘ 问题

一、控制台的报错信息 2021-12-29 15:15:04 [main] ERROR org.springframework.boot.SpringApplication - Application run failed org.springframework.context.ApplicationContextException: Failed to start bean documentationPluginsBootstrapper; nested exception is j…

java图片格式转化(例如jpg格式转化png)

转载自 java图片格式转化&#xff08;例如jpg格式转化png&#xff09; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import java.util.Scanner;import javax.imageio.*; public class FormatConversion {public static final Str…

微软开源PowerShell并支持Linux

建议在Wifi 环境下观看视频 class"video_iframe" data-vidtype"1" style" z-index:1; " height"375" width"500" frameborder"0" data-src"https://v.qq.com/iframe/preview.html?vidv0322g7kd3f&width…

招银网络科技笔试

记录一下 招银网络笔试 2017年09月11日 14:32:53 阅读数&#xff1a;2450 Part1. 30道单选 涉及Java&#xff0c;C&#xff0c;多线程&#xff0c;算法&#xff0c;数据结构&#xff0c;CPU&#xff0c;NP问题&#xff0c;SQL语句&#xff0c;IP地址转换&#xff0c;行测。…

mybatisGenerator逆向工程

一、在pom文件中导入依赖和generator插件 <dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.11</version><scope>test</scope></dependency><dependency&…

2016最佳温情小说:雨还在下....

作者 | 李德霞 来源 | 小小说选刊 哗&#xff0c;一道闪电&#xff1b;轰&#xff0c;一个响雷。 暴雨倾盆&#xff0c;天地间浑沌一片…… 老大扑腾腾坐起来&#xff0c;心也跟着扑腾腾地跳。老大拉亮灯&#xff0c;推推身边的媳妇。媳妇一骨碌爬起来&#xff0c;咋&#xf…