• .net core 单元测试之 JustMock第一篇


    前面介绍了单元测试的框架NUnit,它可以很好的帮助我们建立测试,检验我们的代码是否正确。但这还不够,有时候我们的业务比较重,会依赖其它的类。基于隔离测试的原则,我们不希望依赖的其它类影响到我们的测试目标。这时候Mock就显得十分重要了。当然还有其它因素使得我们必须Mock对象,比如配置文件,DB等。

    提供Mock技术的工具很多:Moq,NSubstitute,RhinoMocks,TypeMock,JustMock等。开源免费的工具功能局限,像Moq,草根专栏 的博客写得很好。这里我选择JustMock,付费版本可以使用高级功能。

    JustMock 开始

    安装 JustMock ,从官网下载,默认安装。

    添加 Telerik.JustMock.dll 引用,在安装目录下,默认为:C:Program Files (x86)ProgressTelerik JustMockLibraries 。

    开启使用高级功能
    image

    为什么需要Mock

    先看我们需要测试的一个方法:

            /// <summary>
            /// 转账
            /// </summary>
            /// <param name="accountA"></param>
            /// <param name="accountB"></param>
            /// <param name="money"></param>
            /// <returns></returns>
            public double TransferAccounts(BankAccount accountA, BankAccount accountB, double money)
            {
                double transferLimit = 50000.0;//转账最高限制
                try
                {
                    var balanceA = accountA.DrawMoney(money);
                    accountB.SaveMoney(money);
                    return balanceA;
                }
                catch (Exception ex)
                {
                    throw new Exception($"转账失败,{ex.Message}");
                }
            }
    

    测试这个方法的逻辑,只需要下面这段代码就可以了:

            private BankAccount bankAccountA;
            private BankAccount bankAccountB;
            [SetUp]
            public void Setup()
            {
                bankAccountA = new BankAccount(1000);
                bankAccountB = new BankAccount(1000);
            }
    
            [Test]
            public void Transfer_Test()
            {
                IBankService bankService = new BankService();
                bankService.TransferAccounts(bankAccountA, bankAccountB, 500);
                Assert.AreEqual(500, bankAccountA.GetBalance());
                Assert.AreEqual(1500, bankAccountB.GetBalance());
            }
    

    但,如果转账的逻辑变了,需要判断是否超过当日限制,那么用户的转账总额就得从数据库或者其它途径获得了,那么可能代码变成这样子:

            private readonly IBankLimitDao _bankLimitDao;//获取限制条件的类
    
            public BankService(IBankLimitDao bankLimitDao)
            {
                _bankLimitDao = bankLimitDao;
            }
    
            /// <summary>
            /// 转账
            /// </summary>
            /// <param name="accountA"></param>
            /// <param name="accountB"></param>
            /// <param name="money"></param>
            /// <returns></returns>
            public double TransferAccounts(BankAccount accountA, BankAccount accountB, double money)
            {
                double transferLimit = 50000.0;//转账最高限制
                try
                {
                    //判断A是否能转账
                    var total = _bankLimitDao.TotalTransferTotal(accountA.AccountId);//获得限制金额
                    if (total >= transferLimit)
                    {
                        throw new Exception($"超过当日转账限额{transferLimit}");
                    }
                    var balanceA = accountA.DrawMoney(money);
                    accountB.SaveMoney(money);
                    return balanceA;
                }
                catch (Exception ex)
                {
                    throw new Exception($"转账失败,{ex.Message}");
                }
            }
    

    这个时候再用真实对象来测试就有点麻烦了。根据隔离原则,我们不希望测试 TotalTransferTotal 方法里的逻辑和它的正确性,它应该在其它地方测试。这时候Mock就显得重要了,我们可以模拟这个对象,并且给它一个恰当的值,让它“正确”执行。

    所以,测试代码变成这样子:

            [Test]
            public void Transfer_Test()
            {
                var bankLimit = Mock.Create<IBankLimitDao>();//模拟对象
                Mock.Arrange(() => bankLimit.TodalDrawTotal(Arg.IsAny<string>())).Returns(500);//设定一个返回值
                IBankService bankService = new BankService(bankLimit);
                bankService.TransferAccounts(bankAccountA, bankAccountB, 500);
                Mock.Assert(bankLimit);
                Assert.AreEqual(500, bankAccountA.GetBalance());
                Assert.AreEqual(1500, bankAccountB.GetBalance());
            }
    

    AAA

    什么是AAA?Arrange、Act和Assert。AAA是单元测试中编写代码的模式。

    • Arrange:准备,设置需要测试的对象。
    • Act:执行测试的实际代码。
    • Assert:验证结果。

    一个简单的例子:
    这个例子包括创建模拟对象,标记为InOrder(),意为必须调用,执行方法,最后用Mock.Assert验证。

    public interface IFoo 
    { 
        void Submit(); 
        void Echo(); 
    } 
    
    [Test]
    public void ShouldVerifyCallsOrder()
    {
        // Arrange 模拟对象,并且设置条件
        var foo = Mock.Create<IFoo>();
    
        Mock.Arrange(() => foo.Submit()).InOrder();
        Mock.Arrange(() => foo.Echo()).InOrder();
    
        // Act 执行代码
        foo.Submit();
        foo.Echo();
    
        // Assert 验证结果
        Mock.Assert(foo);
    } 
    

    编写测试方法的时候尽量遵循AAA的模式编写,可以让测试代码更清晰可读。

    Mock Behaviors

    JustMock 在Mock对象的时候有四种不同的行为可以选择。

    • RecursiveLoose Behavior
      默认的选项。模拟的对象不会出现null对象,递归调用也将创建一个默认的对象、默认值或者空值。
    • Loose Behavior
      除了设置值,否则Loose创建的对象将是默认值。
    • CallOriginal Behavior
      将会采用最初的模拟对象。
    • Strict Behavior
      采用此行为,模拟对象必须设置值,否则会出现 MockException异常。

    下面代码展示不同类型的结果:

            [Test]
            public void Test()
            {
                // Arrange 
                var rlFoo = Mock.Create<FooBase>(Behavior.RecursiveLoose);
                var lFoo = Mock.Create<FooBase>(Behavior.Loose);
                var coFoo = Mock.Create<FooBase>(Behavior.CallOriginal);
                var sFoo = Mock.Create<FooBase>(Behavior.Strict);
    
                Mock.Arrange(() => rlFoo.GetString("y")).Returns("z");
                Mock.Arrange(() => lFoo.GetString("y")).Returns("z");
                Mock.Arrange(() => coFoo.GetString("y")).Returns("z");
                Mock.Arrange(() => sFoo.GetString("y")).Returns("z");
    
                // Act 
                var rlactualX = rlFoo.GetString("x"); // 结果:""
                var rlactualY = rlFoo.GetString("y"); // 结果:"z"
    
                var lactualX = lFoo.GetString("x"); // 结果:null
                var lactualY = lFoo.GetString("y"); // 结果:"z"
    
                var coactualX = coFoo.GetString("x"); // 结果:"x"
                var coactualY = coFoo.GetString("y"); // 结果:"z"
                var coactualA = coFoo.GetString("a"); // 结果:"a"
    
                //var sactualX = sFoo.GetString("x"); // 结果:出现异常
                var sactualY = sFoo.GetString("y"); // 结果:"z"  
    
                var expectedX = "x";
                var expectedY = "z";
    
                // Assert 
                Assert.AreEqual(expectedX, rlactualX);
                Assert.AreEqual(expectedY, rlactualY);
            }
    

    本篇到这,下篇再记录一些其它用法。

  • 相关阅读:
    Python实例 包机制
    Python实例 类和继承
    python实例 文件处理
    python实例 异常处理
    配置 Apache+php多端口多站点(转载)
    C#中Delegate和Event以及它们的区别(转载)
    LINQ to SQL更新数据库操作(转载)
    创业公司如何实施敏捷开发(转)
    利用ps橡皮擦工具快速抠图
    XP win2003系统 微软雅黑字体的使用方法
  • 原文地址:https://www.cnblogs.com/jimizhou/p/11419364.html
Copyright © 2020-2023  润新知