We are having quite a discussion lately within Typemock about the importance of the visibility of tested methods.
For example, should it really be harder to test a private method?
If it is harder, then many developers will opt to change the scope of the tested method to public, even if business wise, it should stay private. If Visual Studio allowed using private methods from test projects (as it does with internal methods) then developers can choose to invoke private methods and the test will not pollute the design. (I know that Visual Studio does have a private accessor, but it is still awkward.)
The same goes with mocking or isolating. During the test, there are many times that we don’t care about some calls and want to fake those out.
Should we care if these calls are static, public, protected or private? If we just don’t care about those interactions (known as stubbing) should we care about the visibility of these methods?
On one hand we are saying that it does matter and that we need to explicitly say this when arranging our tests.
example of a new API with a difference between scopes
1 |
<div><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #008000; ">//</span><span style="color: #008000; "> fake a public instance method </span><span style="color: #008000; "><br /></span><span style="color: #000000; ">RealLogger fake </span><span style="color: #000000; ">=</span><span style="color: #000000; "> MyTest.Make.FakeInstance</span><span style="color: #000000; "><</span><span style="color: #000000; ">RealLogger</span><span style="color: #000000; ">></span><span style="color: #000000; ">();<br />MyTest.WhenCalled(()</span><span style="color: #000000; ">=></span><span style="color: #000000; ">fake.Log(</span><span style="color: #000000; ">""</span><span style="color: #000000; ">)).WillReturn(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">sfsfs</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br /><br /></span><span style="color: #008000; ">//</span><span style="color: #008000; "> fake a non-public instance method</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">MyTest.NonPublic.WhenCalled(fake,</span><span style="color: #000000; ">"</span><span style="color: #000000; ">PrivateInstanceMethod</span><span style="color: #000000; ">"</span><span style="color: #000000; ">).WillReturn(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">l</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br /><br /></span><span style="color: #008000; ">//</span><span style="color: #008000; "> fake a public static method</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">MyTest.Static.WhenCalled(() </span><span style="color: #000000; ">=></span><span style="color: #000000; "> MyType.StaticMethod()).Fake();<br /><br /></span><span style="color: #008000; ">//</span><span style="color: #008000; "> fake a non-public static method</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">MyTest.NonPublic.Static.WhenCalled(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">PrivateStaticMethod</span><span style="color: #000000; ">"</span><span style="color: #000000; ">).Fake();<br /></span></div> |
On the other hand we should keep a minimum API and that will lead us to.
1 |
<div><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #008000; ">//</span><span style="color: #008000; "> fake accessable methods - can use private accesors too</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">RealLogger fake </span><span style="color: #000000; ">=</span><span style="color: #000000; "> MyTest.Make.FakeInstance</span><span style="color: #000000; "><</span><span style="color: #000000; ">RealLogger</span><span style="color: #000000; ">></span><span style="color: #000000; ">();<br />MyTest.WhenCalled(()</span><span style="color: #000000; ">=></span><span style="color: #000000; ">fake.Log(</span><span style="color: #000000; ">""</span><span style="color: #000000; ">)).WillReturn(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">sfsfs</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br />MyTest.WhenCalled(() </span><span style="color: #000000; ">=></span><span style="color: #000000; "> MyType.StaticMethod()).Fake();<br /><br /></span><span style="color: #008000; ">//</span><span style="color: #008000; "> fake any method</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">MyTest.WhenCalled(fake,</span><span style="color: #000000; ">"</span><span style="color: #000000; ">PrivateInstanceMethod</span><span style="color: #000000; ">"</span><span style="color: #000000; ">).WillReturn(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">l</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br />MyTest.WhenCalled(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">PrivateStaticMethod</span><span style="color: #000000; ">"</span><span style="color: #000000; ">).Fake();<br /></span></div> |
My current take is that the visibility issue is a technical one and not a real API issue, I think that the biggest issue developers have is over-specified tests.
What would make developers fall into the put of success is the following:
1 |
<div><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #008000; ">//</span><span style="color: #008000; "> I don't care about any calls to HttpWebRequest in this test<br /></span><span style="color: #008000; ">//</span><span style="color: #008000; "> and all methods will return more Fakes so that there is no NPE</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">MyTest.Fake.Instance</span><span style="color: #000000; "><</span><span style="color: #000000; ">HttpWebRequest</span><span style="color: #000000; "><</span><span style="color: #000000; ">(Members.ReturnFakes);</span></div> |
This will fake all calls to HttpWebRequest, and will automatically recursively fake all ‘chained’ methods, so HttpWebRequest.GetResonse() will return a fke Response, like a recursive NullObject Desgin.
What is your take?