I have occasionally heard or read about people asserting their interfaces in a unit test. I don't mean mocking an interface for use in another type's test, but specifically creating a test to accompany the interface.
Consider this ultra-lame and off-the-cuff example:
public interface IDoSomething
{
string DoSomething();
}
and the test:
[TestFixture]
public class IDoSomethingTests
{
[Test]
public void DoSomething_Should_Return_Value()
{
var mock = new Mock<IDoSomething>();
var actualValue = mock.Expect(m => m.DoSomething()).Returns("value");
mock.Object.DoSomething();
mock.Verify(m => DoSomething());
Assert.AreEqual("value", actualValue);
}
}
I suppose the idea is to use the test to开发者_StackOverflow中文版 drive the design of the interface and also to provide guidance for implementors on what's expected so they can draw good tests of their own.
Is this a common (recommended) practice?
In my opinion, just testing the interface using a mocking framework tests little else than the mocking framework itself. Nothing I would spend time on, personally.
I would say that what should drive the design of the interface is what functionality that is needed. I think it would be hard to identify that using only a mocking framework. By creating a concrete implementation of the interface, what is needed or not will become more obvious.
The way I tend to do it (which I by no means claim is the recommended way, just my way), is to write unit tests on concrete types, and introduce interfaces where needed for dependency injection purposes.
For instance, if the concrete type under test needs access to some data layer, I will create an interface for this data layer, create a mock implementation for the interface (or use a mocking framework), inject the mock implementation and run the tests. In this case the interface serves no purpose than offering an abstraction for the data layer.
I've never seen anything like this but it seems pointless. You would want to test the implementation of these interfaces, not the interfaces themselves.
Interfaces are about well designed contracts, not well-implemented ones. Since C# is not a dynamic language that would allow the interface to go un-implemented at runtime, this sort of test is not appropriate for the language. If it were Ruby or Perl, then maybe...
A contract is an idea. The soundness of an idea is something that requires the scrutiny of a human being at design time, not runtime or test time.
An implementation can be a "functional" set of empty stubs. That would still pass the "Interface" test, but would be a poor implementation of the contract. It still doesn't mean the contract is bad.
About all a specific Interface test accomplishes is a reminder of original intention which simply requires you to change code in 2 places when your intentions change.
This is good practice if there are testable black box level requirements that implementers of your interface could reasonably be expected to pass. In such a case, you could create a test class specific to the interface, that would be used to test implementations of that interface.
public interface ArrayMangler
{
void SetArray (Array myArray);
Array GetSortedArray ();
Array GetReverseSortedArray();
}
You could write generic tests for ArrayMangler, and verify that arrays returned by GetSortedArray are indeed sorted, and GetReverseSortedArray are indeed sorted in reverse.
The tests could then be included when testing classes implementing ArrayMangler to verify the reasonably expected semantics are being met.
In my opinion is not the way to go. A interface is created as an act of refactoring (extract interface) not TDD. So you start by creating a class with TDD and after that you extract an interface (if needed).
The compiler itself does the verification of the interface. TDD does the validation of the interface.
You may want to check out code contracts in c# 4 as you are slightly bordering into that area in how you phrase the question. You seem to have bundled a few concepts together, and you are understandably confused.
The short answer to your question is that you've probably misheard/misunderstood it. TDD will drive the evolution of the Interface.
TDD tests the interface by verifying that coverage is achieved without involving the concrete types (the specific ones that implement the interface).
I hope this helps.
Interfaces are about relationships between objects, which means you can't "test-drive" an interface without knowing the context it's being called from. I use interface discovery when using TDD on the object that calls the interface, because the object needs a service from its environment. I don't buy that interfaces can only be extracted from classes, but that's another (and longer) discussion.
If you don't mind the commercial, there's more in our book at http://www.growing-object-oriented-software.com/
精彩评论