`
wangdeshui
  • 浏览: 255017 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

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代码较多,测试时需要设标志。

 

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

分享到:
评论

相关推荐

    mock学习资料

    1. **Mock的基本概念**:Mock对象是模拟真实对象的行为和响应的假对象。它们用于代替真实的对象,以减少测试中的依赖性和复杂性。Mock对象可以预设返回值,记录方法调用,以及验证方法是否按预期被调用。 2. **单元...

    0积分:Mock对象教程

    1. **减少对外部依赖的直接调用**:Mock对象允许测试人员在不依赖实际数据库连接、网络请求或硬件设备的情况下进行测试,提高了测试的可执行性和速度。 2. **增强测试的可预测性**:通过Mock对象,可以预设特定的...

    C-C++项目的测试驱动开发(TDD):从单元测试到集成测试.md

    在C/C++项目中应用测试驱动开发(TDD)的方法,从单元测试到集成测试,详尽介绍了测试的基础、框架选择、实际应用及最佳实践。文章首先阐述了TDD的核心理念及其优势,包括提升代码质量、减少错误和改进设计等。接着...

    pos-oop-tdd:使用 TDD 创建面向对象的 pos 项目

    面向对象编程(Object-Oriented Programming,简称OOP)是一种编程范式,它基于“对象”的概念,将数据和操作数据的方法封装在一起。在本文中,我们将深入探讨如何使用Test-Driven Development(TDD,测试驱动开发)...

    mock

    Mock是测试驱动开发(TDD)和行为驱动开发(BDD)中的关键元素。它用于模拟那些难以控制或者不存在的外部系统,如网络API、数据库访问等,使得测试更为独立和可控。通过Mock,我们可以在不依赖真实服务的情况下,确保...

    mock-confif

    1. **什么是Mock对象**:Mock对象是模拟真实对象行为的替代品,它们可以预定义返回值、抛出异常或记录方法调用,以符合测试需求。 2. **Mocking框架**:在Java中,有Mockito、PowerMock等框架;在JavaScript中,有...

    Google Mock For Dummies / GMock 傻瓜书

    在软件测试领域,特别是测试驱动开发(Test-Driven Development, TDD)中,Mock 对象和 Fake 对象是两个容易混淆的概念。下面详细介绍两者的区别: - **Fake 对象**:具有实际的工作逻辑实现,但通常采用一些简化的...

    mock学习交流

    1. **隔离性**:Mock对象确保测试只关注待测试代码,避免了因依赖其他组件导致的复杂性。 2. **可预测性**:预设Mock对象的行为,可以准确地控制测试结果。 3. **验证**:通过验证方法调用,确认代码是否正确地使用...

    TDD:通过大量测试寻找最优解决方案.docx

    标题中的"TDD"指的是Test-Driven Development,即测试驱动开发,是一种软件开发方法论,强调在编写实际代码之前先编写测试用例。这种方法的核心理念是通过编写测试来定义需求,确保代码的质量和功能完整性。TDD的...

    Mockito Cookbook

    Mockito是一款广泛应用于Java开发中的测试驱动开发(TDD)工具,它允许开发者创建和配置模拟对象,以便在测试中隔离代码并验证特定方法的行为。这本书详细介绍了如何有效地利用Mockito来编写整洁、可维护的测试代码...

    Test Driven: Practical TDD and Acceptance TDD for Java Developers (PDF英文版)

    《Test Driven: Practical TDD and Acceptance TDD for Java Developers》是一本专注于Java开发者进行测试驱动开发(TDD)和验收测试驱动开发(Acceptance TDD)的专业书籍。这本书以PDF英文版的形式提供,旨在帮助...

    tdd:在 JAVA 中学习 TDD @odd-e

    标题 "TDD:在JAVA中学习TDD @odd-e" 提到的是Test-Driven Development(测试驱动开发,简称TDD)在Java编程语言中的实践。TDD是一种软件开发方法论,强调先编写测试用例,然后编写刚好足够通过测试的生产代码。这种...

    TDD实用jar

    3. MockObjects:MockObjects是早期的模拟对象框架,它的设计理念对后来的模拟框架产生了深远影响。虽然现在它已经被其他更现代的框架所替代,但理解它的概念和用法对于理解模拟对象在TDD中的作用仍然很有价值。 在...

    PyPI 官网下载 | mock-generator-2.2.0.tar.gz

    3. **验证调用**:检查Mock对象的方法是否按预期被调用,以及调用参数是否正确。 4. **集成测试框架**:可能与常见的Python测试框架如unittest、pytest等无缝集成,简化测试代码的编写。 5. **版本管理**:2.2.0版本...

    Ruby-TDD实战TestDrivenDevelopmentinAction

    **Ruby-TDD实战:Test Driven Development in Action** 在软件开发领域,Test-Driven Development(TDD)是一种编程实践,它强调先编写测试用例,然后编写满足这些测试的最小功能代码。Ruby作为一种动态、灵活的...

Global site tag (gtag.js) - Google Analytics