TDD Tip:方法内部New出来的对象如何Mock

简介:

解决的问题:方法内部new的对象在测试时希望能够用mock对象去代替。

问题:以下方法可以解决,但是很是丑陋,各位大侠是否有更好的方法?

描述:如果说把内部的方法放到类的一个公开成员变量,或者放到方法的参数里,我的意见是公开了不应该公开的东西。

使用第三方的类库: Mock工具 Rhino.Mocks, IOC: Castle.Windsor

1. 现在我又这么一段代码,我想测试Math内部这两个方法

    public  class Math

    {

        public long MathAdd(int a, int b)

        {

            Calc c = new Calc();           

            return c.Add(a, b);

        }

 

        public long MathAdd2Price(int a)

        {

            Calc c = new Calc();

            return c.AddCount(a) + c.count;

        }

    }

 

    public class Calc

    {

        public int count = 0;

        public long Add(int a, int b)

        {

            return a + b;

        }

 

        public long AddCount(int num)

        {

            return count + num;

        }

    }

 

2. 我们看到,由于是在内部new的对象,我们就对Calc类产生了很大的依赖,于是我想到注入一个对像,下面是我的设计

 

   public class ContainerFactory

    {

        public static IWindsorContainer container;

        public bool IsDebug = false;

        private static readonly ContainerFactory instance = new ContainerFactory();

 

        public static ContainerFactory Instance

        {

            get

            {

                return instance;

            }

        }

 

        private ContainerFactory()

        {

            AddAllCompent();

        }

 

        private void AddAllCompent()

        {

            if (container == null)

            {

                container = new WindsorContainer();

                //此处职位演示,未使用接口

                container.AddComponentWithLifestyle<Calc>("Calc"LifestyleType.Transient);            }

        }

}

这样我们的Math类可改成这样

  public  class Math

    {

        public long MathAdd(int a, int b)

        {

           // Calc c = new Calc();

            Calc c =(CalcContainerFactory.container["Calc"];

            return c.Add(a, b);

        }

 

        public long MathAdd2Price(int a)

        {

            Calc c = (Calc)ContainerFactory.container["Calc"];

            return c.AddCount(a) + c.count;

        }

 

    }

 

3.  但是,我们如何在测试中用我们Mock的对象代替真实的对象呢?下面是我想的一个自己也认为不好的方法,但能凑活着用

 public class ContainerFactory

    {

        private IWindsorContainer container;

        public bool IsDebug = false;

        private static readonly ContainerFactory instance = new ContainerFactory();

 

        public static ContainerFactory Instance

        {

            get

            {

                return instance;

            }

        }

 

        private ContainerFactory()

        {

            AddAllCompent();

        }

 

        private void AddAllCompent()

        {

            if (container == null)

            {

                container = new WindsorContainer();

                //此处职位演示,未使用接口

                container.AddComponentWithLifestyle<Calc>("Calc"LifestyleType.Transient);            }

        }

 

        #region Calc

        private Calc DebugCalc;

        public Calc    Calc

        {

            get

            {

                if (IsDebug && DebugCalc != null)

                {

                    return DebugCalc;

                }

                else

                {

                    return (Calc)container["Calc"];

                }

            }

            set

            {

                // just for test, for mock object

                if (IsDebug)

                {

                    DebugCalc = value;

                }

                else

                {

                    throw new Exception("just for test");

                }

            }

        }

        #endregion 

    }

修改我们的类

  public  class Math

    {

        public long MathAdd(int a, int b)

        {

           // Calc c = new Calc();

            Calc c = ContainerFactory.Instance.Calc;

            return c.Add(a, b);

        }

 

        public long MathAdd2Price(int a)

        {

            Calc c = ContainerFactory.Instance.Calc;

            return c.AddCount(a) + c.count;

        }

 

    }

 

这样我们来看我们通过了测试的代码

[TestClass()]

    public class MathTest

    {

        [TestMethod()]

        public void MathAddTest()

        {

            Math m = new Math();

 

            // 想让真实代码内部,使用的是Mock的对象

            // Arrange

            MockRepository mocks = new MockRepository();

            Calc mockCalc = mocks.Stub<Calc>();

            mockCalc.count = 5;

            ContainerFactory.Instance.IsDebug = true; //这句很重要

            ContainerFactory.Instance.Calc = mockCalc;

 

            mocks.ReplayAll();

            // Act

            Assert.AreEqual(m.MathAdd(5, 5), 10);

            Assert.AreEqual(mockCalc.AddCount(6), 11);

            mocks.VerifyAll();

 

            Calc mockCalc2 = mocks.Stub<Calc>();

            mocks.ReplayAll();

 

            // 这里有问题,我们希望他是7,但实际是12,因为需要测试中的和实际代码用同一个对象,

            // 他保留上次的状态count的值5

            Assert.AreEqual(mockCalc.AddCount(7), 12);

            mocks.VerifyAll();           

        }

 

        [TestMethod]

    public void MathAddTestActual()

    {

            //这里测试实际使用代码,没用Mock

        Math m = new Math();

        Assert.AreEqual(m.MathAdd(5,6), 11);

        Assert.AreEqual(m.MathAdd2Price(9), 9);

        Calc c = ContainerFactory.Instance.Calc;

        Assert.AreEqual(c.AddCount(5), 5);

        c.count = 20;

        Assert.AreEqual(c.AddCount(5), 25);

        Calc d = ContainerFactory.Instance.Calc;

        Assert.AreEqual(d.AddCount(30), 30);

        Assert.AreEqual(c.count, 20);

        Assert.AreEqual(d.count, 0);

    }

    }

 

 

总结:这样可以不使用用public的类成员变量,不用通过方法参数注入注入对象

 

问题:  ContainerFactory代码较多,测试时需要设标志。

 

其它的问题: 等待高人指出,谢谢!

本文转自敏捷的水博客园博客,原文链接http://www.cnblogs.com/cnblogsfans/archive/2008/12/16/1355640.html如需转载请自行联系原作者


王德水

相关文章
|
测试技术
IDEA创建单元测试与测试覆盖率统计
IDEA(IntelliJ IDEA)不仅支持快速基于当前类创建单元测试,还支持代码测试覆盖率的统计,以及生成报告和标记测试运行命中的代码。
3450 0
IDEA创建单元测试与测试覆盖率统计
|
Python
PyQt绘制股票K线多图Y坐标对齐
PyQt绘制股票K线多图Y坐标对齐
400 0
|
安全 Java API
手把手带你实现第三方应用登录
手把手带你实现第三方应用登录
1516 0
手把手带你实现第三方应用登录
|
测试技术 开发者
单元测试问题之在Mockito中静态方法的调用,如何模拟
单元测试问题之在Mockito中静态方法的调用,如何模拟
replaceAll 的用法总结
replaceAll 的用法总结
|
10月前
|
机器学习/深度学习 监控 算法
现货量化交易机器人系统开发策略逻辑及源码示例
现货量化交易机器人系统是一种基于计算机算法和数据分析的自动化交易工具。该系统通过制定交易策略、获取和处理数据、生成交易信号、执行交易操作和控制风险等环节,实现高效、精准的交易决策。系统架构可采用分布式或集中式,以满足不同需求。文中还提供了一个简单的双均线策略Python代码示例。
|
数据可视化 测试技术 uml
【掌握绘图艺术】用PlantUML绘制完美UML图表,开发者的福音
【掌握绘图艺术】用PlantUML绘制完美UML图表,开发者的福音
3016 1
|
Arthas 监控 前端开发
Arthas学习笔记
Arthas学习笔记
|
存储 缓存 JSON
详解HTTP四种请求:POST、GET、DELETE、PUT
【4月更文挑战第3天】
64653 3
详解HTTP四种请求:POST、GET、DELETE、PUT
|
Java 测试技术 API
Java 新手入门:Java单元测试利器,Mock详解
Java 新手入门:Java单元测试利器,Mock详解
708 1