Proposal/Discussion: Automatic Interfaces #1475
Replies: 22 comments
-
You could also just declare the methods of the class as |
Beta Was this translation helpful? Give feedback.
-
True. But doesn't virtual give the implication that subclasses are supposed to overwrite it's behaviour? Also, the second low level implementation of the proposal would allow you to do this with external classes for which their methods might not have been made virtual. The normal solution would be to create a wrapper class around them and Mock that instead, but again, more boilerplate code. Edit: Also, that methodology is strongly criticised here - https://stackoverflow.com/questions/14451325/should-i-mark-all-methods-virtual |
Beta Was this translation helpful? Give feedback.
-
I think this is a duplicate of #1231 and overlaps #245, so the discussion in both is relevant. |
Beta Was this translation helpful? Give feedback.
-
No, it only signals that subclasses are allowed to override the behavior. If they don't want to, they don't need to. |
Beta Was this translation helpful? Give feedback.
-
Resharper by default will give you warnings about it if you're not overwriting it anywhere, hence I have the impression some people think otherwise |
Beta Was this translation helpful? Give feedback.
-
Can I just ask if this is the optimal solution to this problem, why does everyone seem against it? See the following stack overflow: https://stackoverflow.com/questions/14451325/should-i-mark-all-methods-virtual I believe having automatic interfaces would sidestep the issues they have with virtual methods everywhere. |
Beta Was this translation helpful? Give feedback.
-
In my experience, either option leads to really poor design. Interfaces exist to serve the needs of the client (consuming) code, and should live with the client; their definition should never be driven by the implementing class. |
Beta Was this translation helpful? Give feedback.
-
When you say both options, which two are you referring to? Automatic interfaces and virtual methods everywhere? What would you suggest then, always using interfaces everywhere and accept the large boilerplate code/maintenance time hit? |
Beta Was this translation helpful? Give feedback.
-
Someone has suggested using delegates for all your dependencies as an alternative, but that doesn't seem to be a common/generally recommended solution to the problem |
Beta Was this translation helpful? Give feedback.
-
Hot take: Resharper is bad. |
Beta Was this translation helpful? Give feedback.
-
I think this is the problem:
It's the old pattern of testing an interface which has no reason to exist, other than to test the interface which has no reason to exist. |
Beta Was this translation helpful? Give feedback.
-
Yes, those were the two. I would use tooling to extract an interface, put thought into each interface to keep it client-focused which is usually not hard, and make sure it lives with the client and not the implementation (if it differs). Two other experiences come to mind:
|
Beta Was this translation helpful? Give feedback.
-
Well the goal in this case is to test a class that depends on another class/interface while having as little boilerplate code as possible to maintain. The purpose is not to test the interface or dependent class itself. It comes down to wanting to mock things for unit tests and how to do that. Solutions to how you do mocking of dependencies for unit tests so far have been:
|
Beta Was this translation helpful? Give feedback.
-
I think unit testing with mocks is a bit overrated. At least the way I see it traditionally described. In my opinion you have another option:
"Unit" testing all depends on what you consider to be a unit. I don't think units are classes. Units are distinct systems which are fully contained and have little external dependency. If that describes your whole application, but you think it's too big to be a unit, then work on reducing dependencies. I like units to be assemblies. (Actually I like them to be components but C#/.NET doesn't support that as a distinct concept.) |
Beta Was this translation helpful? Give feedback.
-
Although that is an approach, I think ultimately you end up writing the same (or more likely more) tests to get the same level of code coverage. The difference I see is when you have 1024 possible combinations of inputs to test, the odds are much higher that you'll miss some combinations rather that testing 5 more granular units with 4 inputs each that combine to create the same number of combinations (4 x 4 x 4 x 4 x 4). And with this approach you've only had to write 20 tests instead of 1024. Then your integration tests check that those units are integrating successfully. What is more likely to happen in your approach is that 100 out of the 1000 or so combinations of inputs will be tested and the system ends up being created quickly but with far more things that can potentially go wrong. |
Beta Was this translation helpful? Give feedback.
-
I don't see why there would be a difference. I would have made 4 tests per input either way, unless there is a dependency between the inputs. In that case:
The latter should yield the same total number of tests, assuming the same amount of coverage. |
Beta Was this translation helpful? Give feedback.
-
There are many ways to create seams that allow for pieces of an application to be tested in isolation. Interfaces are one; virtual methods are another; delegates and events are two more. For the (IMHO rare) cases where these are insufficient, there are other ways. Microsoft Fakes is perhaps the best known. I recently stumbled across Pose. Quoting from the project page:
I haven't tried it myself, but it would be worth checking out. |
Beta Was this translation helpful? Give feedback.
-
Yes, and there are often dependencies between units.
Yes which is exactly what I've been talking about this whole thread, how to deal with mocking. |
Beta Was this translation helpful? Give feedback.
-
@lemonlion I have a lot of sympathy for your position, since I spent literally years wishing for the same thing. Now I have come to two opinions about clean code that would make such a feature redundant:
As you've already said we're forced into this scenario because of how we're doing unit testing, which brings me to my second point:
|
Beta Was this translation helpful? Give feedback.
-
Fortunately VS2015 stole the few usable features. They should do that more often. |
Beta Was this translation helpful? Give feedback.
-
uhh @YairHalberstadt @CyrusNajmabadi @333fred what is going on here? |
Beta Was this translation helpful? Give feedback.
-
We're attempting to convert this to a discussion. It is failing to do so 😅 |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
There's a common pattern where class interfaces end up being created for the sole purpose of mocking in unit testing. This ends up creating a lot of boilerplate code as no actual alternative implementation is ever intended beyond the mock in the unit tests. These unit test-only interfaces must then be maintained every time there is a change to the class.
I propose a solution to this where a "default" interface can be defined and generated.
So for example this:
Would be similar to
I used the double bracket syntax (
I<<ClassName>>
) to distinguish it from a generic of a class called 'I', but completely open to syntax suggestions.As far as I see it, this could be implemented in one of two ways:
High level, making it a kind of syntactic sugar. The code would be automatically/invisibly translated at compile time (similarly to the way that extension methods get converted into static classes in c# 7 and below). The disadvantage of this would be that you would only be able to use this syntax on classes that are created by you in your solution as you can't force an interface onto an external class. This would however still be an advantage over the current state of autogenerating interfaces using VS/Resharper and then having to maintain them.
Low level. Make all c# classes implement a default interface of themselves, only accessible via the "default interface" syntax (eg
I<<ClassName>>
). This would allow this syntax to be used on all classes that have been compiled from the correct version of c# upwards.Please note, in my proposal, the interface
I<<DependencyClass>>
would/should be separate to an interface created by the user calledIDependencyClass
. ie there would/should be no namespace conflicts to creating an interface calledIDependencyClass
as well.Beta Was this translation helpful? Give feedback.
All reactions