2012-04-05 47 views
1

比方說,我有一堆看起來像類...單元測試.net中高度耦合的代碼;反射注入模擬?

class Foo{ 
    private Bar highlyCoupled = new highlyCoupled(); 

    public bool DoTheThing(){ 
    return highlyCoupled.doesTheThing(); 
    } 
} 

是否有可能使用反射來打開foo和注入(鴨衝可能是一個更正確的說法)某種mockHighlyCoupled在高度耦合的地方?

怎麼樣在這種情況下...

class DoubleFoo : Foo{ 
    public bool DoTheOtherThing(){ 
    return DoTheThing(); 
    } 
} 

可以繼承highlyCoupled有一個模擬插在它的地方嗎?

不幸的是,重構代碼以便不需要反射是不可能的。

+1

第2行的語法錯誤。 – 2012-04-05 14:28:01

+1

沒有一些嚴重的重構,你的測試可能非常脆弱。 – jrummell 2012-04-05 14:29:28

+0

那你在問什麼?你能用反射來設置私人成員嗎?是的你可以。這樣做有困難嗎? – cadrell0 2012-04-05 14:35:17

回答

1

由於你不能使用模擬框架重構可以使你更容易一點。例如:

TypeMock:

var fakeType = Isolate.Fake.Instance<SomeType>(); 
ObjectState.SetField(fakeType, "_somePrivateField", myValue); 

起訂量:

var fakeType = new Mock<SomeType>() 
fakeType.Protected().Setup<SomeType>("_somePrivateField").Returns(myValue); 

說實話,我並沒有真正用起訂量嘗試了這一點,但我認爲它會做你的需要。

+0

謝謝,這看起來是最有用的答案。它似乎不是一個非常獨特的情況,看它在一個框架中的回答證實了這一點。 – MushinNoShin 2012-04-05 15:35:58

+0

什麼版本的Moq有'Protected()'方法?我只是下載了Moq,而且我需要將Mock注入專用字段,否則這些字段將在運行時由MEF組成。 – IAbstract 2012-04-22 01:35:08

+0

根據文檔應該是最新版本。 http://code.google.com/p/moq/wiki/QuickStart#Miscellaneous – richk 2012-04-23 14:38:16

0

如果你根本無法重構(使highlyCoupled受保護),那麼你就被反射卡住了。這可以讓你在不修改的情況下設置highlyCoupled的值。

+0

內部也會工作。 – bryanbcook 2012-04-05 22:14:20

0

我普遍認同羅布;如果爲了使依賴關係更鬆散地耦合(至少允許像測試代理這樣的派生類來覆蓋它的默認值),您不能重構此屬性,那麼儘管其可見性設置該值的反射幾乎是您的唯一方法可以去。

絕對最不重要的是讓依賴項受到保護。如果現在或將來的任何一點是可能的,做到這一點:

class Foo{ 
    protected Bar highlyCoupled = new highlyCoupled(); 

    public bool DoTheThing(){ 
    return highlyCoupled.doesTheThing(); 
    } 
} 

... 

//in your test suite  
class FooTestProxy:Foo 
{ 
    public FooTestProxy(Bar testMock) 
    { 
     highlyCoupled = testMock; 
    } 
} 

//now when testing, instantiate your test proxy and pass it the mocked object 
1

你的確可以使用反射和嘲笑的類型,但該類型必須從原始類型繼承(FieldInfo.SetValue否則會失敗)。

void Main() 
{ 
    var bar = new Bar(); 
    var type = typeof(Bar); 
    // Get the type and fields of FieldInfoClass. 
    var fields = type.GetFields(BindingFlags.NonPublic | BindingFlags.Instance); 
    fields[0].SetValue(bar, new FooMock()); // you can use new Foo() here too. 
    bar.Print(); 
} 

class Foo { 
    public int i = 0; 
} 

class FooMock : Foo { 
} 

class Bar { 
    private Foo foo = new Foo(); 

    public void Print() { 
     Console.WriteLine(i); 
    } 
}