H3BPM Controllers 单元测试(二)

  • 单元测试的范围

    代码中往往会存在一定的依赖。一个简单的业务可能会依赖于第三方服务,邮件服务、微信服务、钉钉服务等。一个功能模块也可能会和数据库有交互。
    那么问题来了,你的单元测试的范围时什么?
    1. 只是对业务代码进行测试
    2. 对数据库、邮件服务等第三方服务的测试
    3. 对从业务到数据库以及第三方服务的全链路测试

下面,废话不多说,我们上通过实际代码来阐述几种情况:

 //组织机构服务类
    public class OrganizationService : ServiceBase
    {
        /// <summary>
        /// 组织机构依赖的对象IOrganization,数据库交互类,可以理解为EF中的DBSet<Organization>()。
        /// </summary>
        private IOrganization _organization;
        public IOrganization OrganizationManager
        {
            get
            {
                if (_organization == null)
                {
                    _organization = Engine.Organization;
                }
                return _organization;
            }
        }

        public OrganizationService() { }

        /// <summary>
        /// 可以通过Moq框架注入IOrganization进行了测试
        /// </summary>
        /// <param name="organization"></param>
        public OrganizationService(IOrganization organization)
        {
            this._organization = organization;
        }

        /// <summary>
        /// 根据组织类型获取组织列表
        /// </summary>
        /// <param name="unitType">组织类型</param>
        /// <returns>组织列表类型</returns>
        public List<OrgJobViewModel> GetRoleList(UnitType unitType)
        {
            List<OrgJobViewModel> listRole = new List<OrgJobViewModel>();
            List<OThinker.Organization.Unit> listunits = OrganizationManager.GetAllUnits(unitType);
            if (listunits != null && listunits.Count > 0)
            {
                foreach (OThinker.Organization.Unit u in listunits)
                {                 
                    //上级角色
                    OrgPost parentPost = OrganizationManager.GetUnit(post.SuperiorID) as OrgPost;
                    listRole.Add(new OrgJobViewModel { });
                }
            }
            return listRole;
        }
}

需要被测试的服务:OrganizationService 。
OrganizationManager:组织机构管理器,主要用于和数据库交互,其中也包含部分业务逻辑。

GetRoleList:被测试的方法,单元测试需要测试的方法。验证此方法的业务逻辑

明确测试的主体,才能编写健壮和合理的单元测试。
这里我们使用Moq对第三方服务进行Mock,简单的介绍一下Moq框架。

  • Moq是一个用于模拟外部以依赖的框架。
  • 他可以去模拟一个非密封类,接口或者类都可以。
  • 他可以去创建一个空的类,通过 mock.Setup(),来将方法override。
  • 实际方法调用的参数,或者调用的实体对象还是需要自己通过代码去模拟。

针对GetRoleList业务逻辑

        [TestMethod]
        public void GetRoleList_HasList_Return_List()
        {
            //1 创建mock对象
            Mock<IOrganization> mock = new Mock<IOrganization>();
            List<Unit> units =CreateOrgPostList();
            // 2 设置需要被Mock的方法
            mock.Setup(m => m.GetAllUnits(UnitType.Post)).Returns(units);
            // It.IsAny<string>() 参数为任意的string
            // (string s) => units.FirstOrDefault(d => d.ObjectID == s) 当参数为s时,返回 units.FirstOrDefault(d => d.ObjectID == s);
            mock.Setup(m => m.GetUnit(It.IsAny<string>())).Returns((string s) => units.FirstOrDefault(d => d.ObjectID == s));
            //3 注入Mock
            OrganizationService organization = new OrganizationService(mock.Object);
            var list = organization.GetRoleList(UnitType.Post);
            Assert.AreEqual(units.Count, list.Count);
            Assert.AreEqual(units[0].ObjectID, list[0].ObjectID);
        }

我们通过Moq框架将IOrganization注入到OrganizationService 中,将关注点聚焦到GetRoleList的业务逻辑上,无论数据库是否交互成功,第三方服务是否畅通,业务逻辑测试通过,单元测试即通过。
优点:不依赖于第三方;代码解耦。
缺点:针对数据库交互服务,需要其他单元测试覆盖

针对GetRoleList业务逻辑以及数据库交互

        [TestMethod]
        public void GetRoleList_HasList_Return_List()
        {       
            List<Unit> units =CreateOrgPostList();
            OrganizationService organization = new OrganizationService();
            //将数据实例化到数据库中
            organization .SaveUnits(units);
            var list = organization.GetRoleList(UnitType.Post);
            Assert.AreEqual(units.Count, list.Count);
            Assert.AreEqual(units[0].ObjectID, list[0].ObjectID);
             //将测试数据清空
            organization .DeleteUnits(units);
        }

这种针对的测试点是GetRoleList的业务逻辑以及OrganizationService 和数据库的交互逻辑,因为我们这里测试的数据是真实的保存到数据库并且从数据库中取出来。
优点:一次性对整个方法进行测试,保证方法的可靠性
缺点:代码过于耦合,并且在模拟数据上工作量较大

想清楚你需要测试的主体,单元测试的粒度,才能书写出健壮可靠的单元测试。

  • 单元测试的目的

    基于单元测试的主体来分析,是为了保证测试的单元功能正常,在进行修改后,可以通过自动化测试来保证功能的可靠性。可以将一些重复的或者繁琐的手工操作通过代码来进行。基于代码的层面去分析代码的正确性。
    因此,在划分单元时就就显得至关重要。
    就拿上面的例子来讲:
    OrganizationService 为一个单元,验证功能的完整以及可靠性
    或者
    OrganizationService 为一个单元,验证OrganizationService 的业务逻辑。
    IOrganization 为一个单元,验证数据库交互的可靠性。

    划分的单元粒度不同,测试目的也不同。

  • 单元测试的框架

    我们使用的测试框架为微软的MSUNNIT
    Mock 框架是 Moq

    参考:
    Moq QuickStart
    MSUNIT

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容

  • 一、百变怪 Mockito Mockito可谓是Java世界的百变怪,使用它,可以轻易的复制出各种类型的对象,并与...
    罗力阅读 3,911评论 3 18
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,093评论 1 32
  • 简介 测试目的:模拟多种可能性,减少错误,增强健壮性,提高稳定性。 测试种类:在iOS中的通常分为单元测试和UI测...
    i顺颂时宜阅读 9,109评论 0 39
  • 泰戈尔在《飞鸟集》中说过“当你为错过太阳而哭泣的时候,你也要再错过群星了”。 人生路很漫长,漫长的途中我们...
    冀林路阅读 221评论 4 2
  • -01- 再见小听,恍然间已隔六年时光了。 那时候我们挤在公交车的扶手上,痛骂喝人血的老板,他翘着双腿喝酸奶,而我...
    Nico尼可阅读 2,488评论 1 17