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代碼較多,測試時需要設標志。
?
其它的問題: 等待高人指出,謝謝!
本文轉自敏捷的水博客園博客,原文鏈接http://www.cnblogs.com/cnblogsfans/archive/2008/12/16/1355640.html如需轉載請自行聯系原作者
王德水
總結
以上是生活随笔為你收集整理的TDD Tip:方法内部New出来的对象如何Mock的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2017202110104-高级软件工程
- 下一篇: SCCM2012SP1---配置客户端发