zoukankan      html  css  js  c++  java
  • 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 =(Calc) ContainerFactory.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代码较多,测试时需要设标志。

     

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

  • 相关阅读:
    Git删除不存在对应远程分支的本地分支
    Git删除远程分支
    将博客搬至CSDN
    HttpStatus
    Mysql 日期
    jekyll开发静态网站
    修改maven默认的jdk版本
    使用@Value进行静态常量的值注入
    妙笔生花处,惊艳四座
    Integer 和 int 值比较
  • 原文地址:https://www.cnblogs.com/cnblogsfans/p/1355640.html
Copyright © 2011-2022 走看看