Unit Test Via Visual Studio-Part5

本文主要介绍Visual Studio(2012+)单元测试框架的一些技巧:

  1. 如何模拟类的静态构造函数
  2. 如何测试某方法被调用过
  3. 如何测试某方法执行的次数
  4. 并行编程测试注意事项

一、如何模拟类的静态构造函数

1.1 被测代码

namespace BlogDemo.UTDemo.Tricks
{
    public class StaticConstructorExample
    {
        static StaticConstructorExample()
        {
            //step1:read data from web service
            //step2:save data into database
        }

        public int DoSomething()
        {
            return 0;
        }
    }
}

上面的类有一个静态构造函数,里面做两件事情:

  1. 从某webservice读取数据
  2. 将读取到的数据保存到数据库。

以上两件事情都是对外界环境的严重依赖

我们需要测试的方法是DoSomething,需要测试这个类,就需要实例化StaticConstructorExample这个类,实例化这个类之前,其静态构造函数将会被自动执行:这个点就是问题。

  1. 静态构造函数依赖外部环境,这个外部环境有可能在某一天就挂了
  2. 如果外部环境挂了,静态构造函数就会报错,那我们的单元测试就无法按照预期进行运行
  3. DoSOmething本身不直接依赖外部环境,但是通过静态构造函数间接依赖了外部环境,这违反了单元测试repeatable原则(当外部环境挂了,我们的测试就无法运行)

解决办法依然是Shim,通过ms.test的Shim可以模拟一个类的静态构造函数,从而改变静态构造函数的行为,消除外部依赖。

1.2 测试代码

 [TestClass]
    public class StaticConstructorExampleTests
    {
        [TestMethod]
        public void DoSomethingTest()
        {
            using (ShimsContext.Create())
            {
                var isStaticConstructorExecuted = false;
                ShimStaticConstructorExample.StaticConstructor = () =>
                {
                    //step1:mock web service
                    //step2:mock database
                    isStaticConstructorExecuted = true;
                };
                var example = new StaticConstructorExample();
                var data = example.DoSomething();
                Assert.AreEqual(0, data);
                Assert.IsTrue(isStaticConstructorExecuted);
            }

        }
    }

上面的代码是通过ShimClassName.StaticConstructor来实现对静态构造函数进行模拟的。

二、如何测试某方法被调用过

2.1 被测代码

基础代码,在DemoClass中进行调用

    public interface IGoodActionHandler
    {
        void Action();
    }

    public interface IBadActionHandler
    {
        void Action();
    }

    public class GoodHandler : IGoodActionHandler
    {
        public void Action()
        {
            throw new NotImplementedException();
        }
    }

    public class BadHandler : IBadActionHandler
    {
        public void Action()
        {
            throw new NotImplementedException();
        }
    }

要被测试的类:

  public class DemoClass
    {
        IGoodActionHandler goodHandler;
        IBadActionHandler badHandler;
        public DemoClass()
        {
            this.goodHandler = new GoodHandler();
            this.badHandler = new BadHandler();
        }
        public void DoSomething(int type)
        {
            if (type == 1)
            {
                DoSomethingGood();
            }
            else
            {
                DoSomethingBad();
            }
        }

        private void DoSomethingGood()
        {
            this.goodHandler.Action();
        }

        private void DoSomethingBad()
        {
            this.badHandler.Action();
        }
    }

要测试上面的DoSomething方法,上面DoSomethingGood和DoSomethingBad都依赖自接口,在测试的时候都可以进行mock(stub)。在侧测DoSomething方法是只需要验证当Type为1时执行了goodHandler的action,否则执行badHandler的Action。

这也是单元测试的一个关键点:关注单元。这里不关注goodhandler 和badhandler的内部逻辑(这两个handler的内部逻辑可以单独测试,属于另外的单元),这里只关注是否按照逻辑路由到了正确的handler。

2.2测试代码

  [TestClass]
    public class DemoClassTests
    {
        [TestMethod]
        public void DoSomething_DoSomethingGood_Tests()
        {
            var goodHandlerExecuted = false;
            StubIGoodActionHandler goodHandler = new StubIGoodActionHandler()
            {
                Action = () =>
                {
                     goodHandlerExecuted = true;//如果执行了goodhandler则置为true
                }
            };
            var badHandlerExecuted = false;
            StubIBadActionHandler badHandler = new StubIBadActionHandler()
            {
                Action = () =>
                {
                    badHandlerExecuted = true;//如果执行了badhandler则置为true
                }
            };
            var demoClass=new DemoClass();
            demoClass.BadHandler=badHandler;//注入badhandler
            demoClass.GoodHandler=goodHandler;//注入goodhandler
            demoClass.DoSomething(1);
            Assert.IsTrue(goodHandlerExecuted);//执行了goodhandler
            Assert.IsFalse(badHandlerExecuted);//没有执行badhandler
        }
    }

上面使用了两个技术

  1. 面向接口编程,使用Stub技术,动态注入Stub(打桩),在测试的时候对对象进行了模拟
  2. 改变了Stub对象的行为,本文只是通过在Stub对象的内部设置标志位的值来表示是否执行了这一个步骤。

上面的标志位的方法Shim技术照样使用,静态构造函数的模拟就是通过Shim后在构造函数内部修改标识位来验证构造函数被执行的。接下来介绍的测试执行次数也是通过shim后修改标识位来实现的

三、如何测试某方法执行的次数

3.1 被测代码

  public class DemoClass
    {
        private LoopHandler handler;
        public DemoClass()
        {
            handler = new LoopHandler();
        }
        public void DoSomething(int times)
        {
            for (var i = 0; i < times; i++)
            {
                handler.Do();
            }
        }
    }

    public class LoopHandler
    {
        public void Do() { }
    }

现在要验证DoSomething是否执行了times次Do方法。

3.2 测试代码

 [TestMethod]
        public void DoSomething_RunTimes_Test()
        {
            using(ShimsContext.Create())
            {
                var times = 0;
                ShimLoopHandler.AllInstances.Do = (@this) =>
                {
                    times++;//每进入一次加一次
                };
                var givenTimes = 100;
                new DemoClass().DoSomething(givenTimes);
                Assert.AreEqual(times, givenTimes);
            }
        }

思想和上面Stub是一样,在Shim模拟的方法内部进行计数器相加。

3.3 并行编程测试注意事项

这里有一点需要注意一下,因为int的++不是线程安全的,如果把DemoClass的循环修改成多个线程并行执行的话测试代码需要做相应的调整。,对times++进行lock。

 public void DoSomething(int times)
        {
            Parallel.For(0, times, (item) =>//并行执行
            {
                handler.Do();
            });
        }

 [TestMethod]
        public void DoSomething_RunTimes_Test()
        {
            using(ShimsContext.Create())
            {
                var times = 0;
                ShimLoopHandler.AllInstances.Do = (@this) =>
                {
                    lock (this)//lock线程安全
                    {
                        times++;//每进入一次加一次
                    }
                };
                var givenTimes = 100;
                new DemoClass().DoSomething(givenTimes);
                Assert.AreEqual(times, givenTimes);
            }
        }

上面的代码当Times不是特别大的时候一般不加lock也是可以的,不是特别大的时候一般不会产生线程安全问题。但是当times比较大的时候不加lock就会出问题。

当times是100000时去掉lock。我运行就出现了问题:

测试就是为了严谨,如果有上面并行的问题,测试的时候还是加上lock比较好。

时间: 2024-10-11 22:39:38

Unit Test Via Visual Studio-Part5的相关文章

Visual Studio 中的单元测试 UNIT TEST

原文:Visual Studio 中的单元测试 UNIT TEST 注:本文系作者原创,可随意转载,但请注明出处.如实在不愿注明可留空,强烈反对更改原创出处. TDD(Test-Driven Development) 测试驱动开发是敏捷开发中的一项核心实践和技术,也是一种设计方法论.TDD的原理是在开发功能代码之前,先编写单元测试用例代码,测试代码确定需要编写什么产品代码.单元测试是最基本的测试步骤.位于整个产品开发流程V模型的最底部.大致如图,在各种开发流程中RA&PSD完成后,无需底层基础,

Visual Studio 2017 发布

https://www.visualstudio.com/zh-cn/news/releasenotes/vs2017-relnotes 发布日期:2017 年 3 月 7 日 摘要 开发:快速导航.编写并修复代码 新的安装体验 - 降低了最小内存需求量以实现更快.更定制化的安装,并且支持脱机安装. Visual Studio IDE - 大幅改进了 Visual Studio 2017,包括减少启动和解决方案加载时间.改进登录和标识.改进代码导航以及增添打开文件视图和链接的服务,实现了应用和任

Recommended add-ons/plugins for Microsoft Visual Studio [closed]

SmartPaster - (FREE) Copy/Paste code generator for strings AnkhSvn - (FREE) SVN Source Control Integration for VS.NET VisualSVN Server - (FREE) Source Control ReSharper - IDE enhancement that helps with refactoring and productivity CodeRush - Code ge

Visual Studio 2017

软件标签: Visual Studio是由微软推出的代码编程工具,Visual Studio 2017 有着更多的趣味性,支持 C#.C++.Python.Visual Basic.Node.js.HTML.JavaScript 等绝大多数的主流代码语言,当然VS 2017在重构.代码修复和调试的增强为你带来更多的便利.无论是个人还是企业都能很好的驾驭它,新云带来Visual Studio 2017 正式版下载,需要的不要错过. visual studio 2017功能特点 1.开发 快速导航.

[入门级] 基于 visual studio 2010 mvc4 的图书管理系统开发初步 (二)

[入门级] 基于 visual studio 2010 mvc4 的图书管理系统开发初步 (二) Date  周六 10 一月 2015 By 钟谢伟 Category website development Tags asp.net / mvc4 相关资源 ibatis manual pro git 廖雪峰的官方网站 BookMS-V1.0 上一篇链接 任务简介 开发工具:VS2010 项目框架:MVC4 浏览器:Chrome 数据库ORM框架:iBatis.net 数据库:mysql 后端开

【MVC 4】4.MVC 基本工具(Visual Studio 的单元测试、使用Moq)

 作者:[美]Adam Freeman      来源:<精通ASP.NET MVC 4> 3.Visual Studio 的单元测试 有很多.NET单元测试包,其中很多是开源和免费的.本文打算使用 Visual Studio 附带的内建单元测试支持,但其他一些.NET单元测试包也是可用的. 为了演示Visual Studio的单元测试支持,本例打算对示例项目添加一个 IDiscountHelper 接口的新实现. 在 Models 文件夹下新建类文件 MinimumDiscountHelpe

【翻译】在Visual Studio中使用Asp.Net Core MVC创建你的第一个Web API应用(一)

HTTP is not just for serving up web pages. It's also a powerful platform for building APIs that expose services and data. HTTP is simple, flexible, and ubiquitous. Almost any platform that you can think of has an HTTP library, so HTTP services can re

软件工程-构建之法 Visual Studio开发平台的安装与单元测试

一.前言 自从开始了大三下的生活,学校开设一门课程“软件工程”,刚好我们是第一届进行课程改革,不在像以前那样背背概念,考前进行好好突击,然后考试就能过,最后毕业了发现软件工程课程到底我们在其中学习了什么. 我自己觉得改革,会不会让自己觉得考的不好,能学到啥?在老师的第一节课上,老师把整个学习蓝图描述一下,我大约感觉到这才是一个大学的课堂,不仅仅子啊 课堂上听老师讲课,最重要的是自己在课后自己在学习,自己在网上自己寻找知识,进行学习.自己才是这门课程的主人,主动学习课程,不是被动听老师讲. 喜欢编

Python 在Visual studio 中做单元测试进行TDD开发

Unit Tests Steve Dower edited this page on 14 Jul · 3 revisions Pages 38 Home Azure Remote Debugging AzureSDK Bottle and Azure Table Storage on Azure Bottle and MongoDB on Azure Browsing Code Using PTVS Build Instructions for PTVS Cloud Project Code

使用 Visual Studio Team Test 进行单元测试和java中的测试

原文:使用 Visual Studio Team Test 进行单元测试和java中的测试 C#中test测试地 方法一. 1.从NUnit官网(http://www.nunit.org/index.php)下载最新版本NUnit,当前版本为NUnit2.5.8. 2.安装后,在VS2008中新建测试项目StartNUnit 3.右击项目选择属性,在打开的窗口中选择调试.如图: 4.选择启动外部程序,并定位到NUnit的启动程序nunit.exe.如图: 5.在项目中添加NUnit引用,如图: