Why You Might Not Need Interfaces in C# 12

  Рет қаралды 65,784

Nick Chapsas

Nick Chapsas

10 ай бұрын

Check out my courses: dometrain.com
Become a Patreon and get source code access: / nickchapsas
Hello everybody, I'm Nick, and in this video, I will show you a demo of what might be possible if the Interceptors feature of C# 12 actually makes it into the language. One of the biggest problems with C# code is interface bloat for no apparent reason other than just testability.
Interceptor video: • The New “Interceptors”...
Workshops: bit.ly/nickworkshops
Don't forget to comment, like and subscribe :)
Social Media:
Follow me on GitHub: bit.ly/ChapsasGitHub
Follow me on Twitter: bit.ly/ChapsasTwitter
Connect on LinkedIn: bit.ly/ChapsasLinkedIn
Keep coding merch: keepcoding.shop
#csharp #dotnet

Пікірлер: 293
@phizc
@phizc 10 ай бұрын
IMPORTANT INFO: You're NOT supposed to write the attribute with the file path/line/column yourself. You're supposed to use a source generator to implement the interceptor for you. The source generator runs whenever anything changes un the source code, so it will update the location(s) if the interceptons. If you use source generators as you're supposed to, changing the code will not break it.
@officemishler3364
@officemishler3364 10 ай бұрын
Awesome, I was just writing an attribute to do this work for you at run time. Maybe both approaches can be useful
@rafae5902
@rafae5902 9 ай бұрын
Having to update the path/line/column every time the file changes would be insanity.
@KieranDevvs
@KieranDevvs 10 ай бұрын
The one problem I see with this is that its going to be confusing to people who aren't expecting interceptions. For example, when mocking, you pass a stub into the constructor of the type you want to mock and thus its very clear why you get a certain result from a method call. In this case, your object is null yet you don't get expected behaviour when calling members on null. I guess you can step into the method call to figure out what's going on but it feels "wrong". Maybe they can add some tooling into the debugger to make this less weird? Also, what happens if two interceptors are registered for the same invocation? I assume the last registered interception is the one that executes. This would also lead to weird behaviour where I think tooling is necessary to make it obvious.
@tcortega
@tcortega 10 ай бұрын
It is necessary. Usually, we'd use mock libraries but these mock libraries would be most likely source generators that create interceptors behind the scenes. But I feel you, it feels weird and overengineered.
@KieranDevvs
@KieranDevvs 10 ай бұрын
@@tcortega I don't dislike the idea, I just want to see it refined more, to remove the oddities from the feature before it gets shipped. I don't think its current state is appropriate as null invocations not leading to NRE's is black magic at best.
@modernkennnern
@modernkennnern 9 ай бұрын
If two interceptors are registered on the same target, there's a compile-error, because what would constitute "last"? Ordered by class name? Wouldn't really make sense. This might change in a future iteration of the feature however
@kRySt4LGaMeR
@kRySt4LGaMeR 2 ай бұрын
Exactly, there's a lot of implicit trickery going on. At least with mocks we know the thing is mocked.
@DavidZidar
@DavidZidar 10 ай бұрын
Mocks are overused, agreed. But I would much rather still use an interface and have a reusable fake in-memory implementation instead. This reduces the amount of setup code and performs better and is easier to understand. One potential danger is if the fake implementation and the real implementation differ, but mocks or interceptors can be just as bad in this regard.
@xeekk
@xeekk 10 ай бұрын
I agree, because once I’ve developed near full test coverage I’m mocking most of the repository and the amount of mock code would be about the same as a cheap memory implementation of the repository.
@AlgoristHQ
@AlgoristHQ 10 ай бұрын
This is where containerization comes into play. Don’t mock repos! Just used a containerized database!
@xeekk
@xeekk 10 ай бұрын
@@AlgoristHQ I use Testcontainers for that so I can keep it playable in visual studio without extra setup.
@AlgoristHQ
@AlgoristHQ 10 ай бұрын
@@xeekk I use the built in docker support in vs2022 and docker-compose.
@xeekk
@xeekk 10 ай бұрын
@@AlgoristHQ I need my team to have it as simple as hitting run in visual studio and I also want to see test coverage in app code with NCrunch, so I’ve been migrating away from needing docker compose for integration tests.
@andrewjosephsaid788
@andrewjosephsaid788 10 ай бұрын
I really like the idea. One way to solve it would be that alongside each interceptor is a public static Func (public static Func null) for the first test and mocker.SetupGetBySlug(slug => new Movie() {...}) for the second. On dispose the mocker clears the Funs so that the interceptors call the original method.
@qj0n
@qj0n 10 ай бұрын
That's how it would probably work, but you can't run parallel tests in one process easily Unless you detect it by thread/context and choose proper mock
@andersborum9267
@andersborum9267 9 ай бұрын
While an interesting feature, and definitely one that'll be used in C# 12, the use of an interface is the proper approach here; there''s so much setup and magic happening, that the traditional and well known interface abstration/mocking approach comes across as simple and straight forward.
@michalkowalik89
@michalkowalik89 10 ай бұрын
this interceptors are fishy. I thik when they come up they will lead to very bad spaghetti like projects where a lot of thinks happen like magic. c# is static compiled language so you can refactore anything you want with easy and you do not have to worry that something breaks. adding interface should not be problem in legacy code.
@vorontsovru270895
@vorontsovru270895 10 ай бұрын
6:41 THIS is a pretty good easter egg =)
@alphaios7763
@alphaios7763 10 ай бұрын
the Doug flash when you said "THIS" made me laugh like crazy!
@nickchapsas
@nickchapsas 10 ай бұрын
I'm so glad someone appreciates it. I thought of it during editing and I couldn't stop laughing
@felipet391
@felipet391 10 ай бұрын
I am really excited!!
@FilipSzymaniak
@FilipSzymaniak 10 ай бұрын
interesting feature, going to check interceptors out soon, thanks!
@dagochen
@dagochen 10 ай бұрын
@nick you have your MovieService which has a Create(Movie movie) method, but it just validates and does not create an actual movie object (it already is passed to him). Is that on purpose or just because it does not matter for the purpose of this video? I would expect that a create method creates instances (if validation succeeded) and gives the then created object to the repository.
@aronsz
@aronsz 10 ай бұрын
Aren't you advertising the wrong tool for an already solved problem?
@XXnickles
@XXnickles 10 ай бұрын
While I do with the premise (abuse of interfaces) I disagree with the interception to add "domain" functionality. Does somebody remember/used PostSharp? The main problem with interception is you are adding "sorcery" that is not obvious unless you explicitly point it out. In my case, I am moving towards only using interfaces just for IO and branching away from things like repositories.
@michaldivismusic
@michaldivismusic 10 ай бұрын
I'm just not seeing the value, at least not yet. Let's say the interceptors will be source generated and the usage will be simple. Still, I'd rather mock using Moq or Nsubstitute and setup the mocked methods within the test (if they need to differ between tests) than have a separate interceptor class with the mocking logic. It may just be too much magic for my taste. But I do agree that this could benefit people working on legacy codebases. One problem though, how do you use this new feature on a legacy project unless you migrate it to .NET 8?
@ChenfengBao
@ChenfengBao 10 ай бұрын
The separate intercepting class is supposed to be source generated. You won't write a separate class manually. The experience (ideally) should be the same as the current mocking approach, except that there's no need for interfaces. Interceptor is a compiler feature, so it doesn't actually require .NET 8. It's just released with .NET 8. You can totally use a new compiler on an old code base on .NET Framework 4 for example.
@michaldivismusic
@michaldivismusic 10 ай бұрын
@@ChenfengBao alright, that does make it sound much better.
@kocot.
@kocot. 9 ай бұрын
ms fakes do the legacy part already without updating to .net 8, end of strory
@DarraghJones
@DarraghJones 10 ай бұрын
This will be a real life saver for my legacy C# 12 apps 😬
@marcusmajarra
@marcusmajarra 10 ай бұрын
I can see the value of new interception-based frameworks to help test code that wasn't written to be testable. That being said, I wouldn't use such a tool to inform my future designs as this is not a feature that replicates well across other OO languages. Sure, DI introduces a lot of abstractions, but the same design can be consistently replicated in different languages to achieve the same end result. Furthermore, it allows code to express itself as having no stakes in a particular implementation of a dependency, which is something you might miss out on if you decide to leverage interception just to avoid creating abstractions. Is this code actually tailored for a specific DBMS or do we not care? The abstraction tends to make that question clear. Abstractions also reduce the scope of the refactoring effort when changes need to be applied and isolates the associated risks. I don't feel like providing less abstractions provides such a significant benefit to warrant going with interceptions as the default. That being said, I think that interception-based testing will be an interesting option in at least two cases that I can envision: 1) Intercepting calls to static methods or other non-mockable constructs provided by a framework. This is fairly straightforward: we use a framework to avoid reinventing the wheel, so why should testing code that leverages a framework also have to test the wheel? Shouldn't this be the handled by framework developers? 2) When performance is critical and the overhead of adding and managing abstractions incurs a cost that needs to be avoided. Sometimes, you might need for code to be as fast as possible. And this means reducing the number of virtual calls and reducing the number of collaborators. This often makes code less testable without a setup that correctly replicates production-level conditions. With interceptors, you can verify the interactions that your code introduces in such contexts.
@luvincste
@luvincste 10 ай бұрын
are you saying this stuff works only on source code, if the attribute needs the path to the file and the position, because that would be quite a limitation
@vasilymeleshko
@vasilymeleshko 10 ай бұрын
Is this logic need make interceptors inside one project or can be just injected from above? I mean does this allow hack usual calls between 2 different dll's calls?
@phizc
@phizc 10 ай бұрын
Interceptors only work in the project in which they're implemented. They change the compilation of that project. That's why Nick had to add them to the main project instead of the unit test project.
@CrIMeFiBeR
@CrIMeFiBeR 10 ай бұрын
if the method moves where the interceptor point's to, will the test fail? will the interceptor break? To me it feels like a fancy GOTO, but that is probably me.
@lambda-snail
@lambda-snail 10 ай бұрын
The interceptor feature sounds a bit dangerous, but I'm open minded and interested in seeing how the feature develops. However, we can even now use tools such as passing delegates and using classes with virtual methods to create testable code :)
@squadwuschel
@squadwuschel 10 ай бұрын
Yes I am also using virtual methods to run unit tests and don't use interfaces
@PticostaricaGS
@PticostaricaGS 9 ай бұрын
I think interceptors may be very useful in code self-analysis and something in the lines of memory inspection and performance-counters alike
@EarthCitizen124
@EarthCitizen124 10 ай бұрын
I dont remember did you explain it in previous video, but... What if I've chaged the file a little bit... should I fix all interceptor data (line and position) for each of hundreds tests where it uses?)
@nickchapsas
@nickchapsas 10 ай бұрын
It's all auto generated. You don't have to write the location, line or character by hand
@EarthCitizen124
@EarthCitizen124 10 ай бұрын
​@@nickchapsasin this case it makes sense... thanks!)
@realsk1992
@realsk1992 10 ай бұрын
What kind of Result class do you use?
@CapsAdmin
@CapsAdmin 7 ай бұрын
I feel in this scenario it's a really roundabout and magical way of replacing a function at runtime. This is done in other languages, and so perhaps the warts of doing that translate well over to this novel method.
@kelbobk5
@kelbobk5 10 ай бұрын
You can 'mock' statics. Just point your actual static property/method to an updateable static method and then set this method to what you need for testing. Done this for years for dates and principals. No need to pollute your class/constructor with bloat interfaces.
@sumeshk2653
@sumeshk2653 10 ай бұрын
Can you point to an article or an example in any blog/video/stack overflow which shows this (Need not necessarily be this as main topic, but the sample shows an implementation )? I am very much interested to explore this option for my test cases.
@chris-pee
@chris-pee 10 ай бұрын
What's "an updateable static method"?
@kelbobk5
@kelbobk5 9 ай бұрын
@@chris-pee namespace System; using System.Diagnostics.CodeAnalysis; // you don't need to pollute with yet another interface in the ctor and tidies up the code. Can be with all non domain specific code [ExcludeFromCodeCoverage] public static class DateTimeOffset_ // Can be called help or anything { // Needs this order private static Func _CurrentDateTimeFunction = () => DateTimeOffset.Now; // Actual call public static DateTimeOffset Now => _CurrentDateTimeFunction(); // set it back to the actual DateTimeOffset.Now public static void UseDefault() => _CurrentDateTimeFunction = () => DateTimeOffset.Now; // Call in the arrangement to set the date ti its test value public static void SetDateTime(DateTimeOffset datetime) => _CurrentDateTimeFunction = () => datetime; }
@JVimes
@JVimes 10 ай бұрын
Love it. There's a reason I don't want to use interfaces everywhere: it implies my code is ready for all sorts of implementations passed in, for everything I want to test. It's not. That's lots more work.
@qj0n
@qj0n 10 ай бұрын
Well, if it's not then you probably will have troubles with mocking it anyway
@robwalker4653
@robwalker4653 9 ай бұрын
@@qj0n I think the point John is making is by making an Interface just so you can test something, you are signalling this is an Interface and others may start writing other implementations that you don't want them to. Using interfaces all over the place for mocking tests is not what Interfaces were originally created for.
@qj0n
@qj0n 9 ай бұрын
@@robwalker4653 for me intending to replace a class and writing it in a way, which allows to replace it are two separate things. Even if I don't intend to replace a repository I still end up with repository possible to be replaced as this is a direct result of separating the conserns - repository should hide complexity of database and expose simple methods. I usually use interface here as I want to explicitly control the contract from the side of consumer
@nickbarton3191
@nickbarton3191 10 ай бұрын
Interesting, I was using test harnesses in the 80s that could auto-write interceptors, well procedural 'C'. I can see this would be useful for legacy, saves creating seams. To find out how automate this (just not doing this by hand) presumably watch the other video?
@simenmailundsvendsen9358
@simenmailundsvendsen9358 10 ай бұрын
I absolutely deeply hate this feature. It is way too magic, and I can see all the ways it will be abused to create unreadable and confusing code. Dependency injection and mocking has worked fine so far, I don't see how this solves anything.
@modernkennnern
@modernkennnern 10 ай бұрын
You are not supposed to - literally in the proposal - use this manually. This is intended for use by libraries to have compile-time logic as opposed to runtime. Things like middleware is already "impossible" to understand the flow of, and this feature actually makes it easier to understand by allowing it to actually output understandable source code instead of reflection magic.
@samsonmayeem8409
@samsonmayeem8409 9 ай бұрын
😢the feature is good, I'm developing a new design principal
@bondarenkodf
@bondarenkodf 9 ай бұрын
so, don't use it then.
@modernkennnern
@modernkennnern 9 ай бұрын
@@bondarenkodf to be fair, this is one of those features that you'll have to work with even if you hate it, as the primary reason for its existence is for use in Microsoft's own libraries like AspNetCore.
@womp6338
@womp6338 9 ай бұрын
Dependency injection is magic as well. There’s lots of magic stuff happening that you are required to know about in most code bases
@vitek0585
@vitek0585 10 ай бұрын
Looks great in terms of testing the code where is used third party library or testing legacy code. This feature may be helpful
@MaQy
@MaQy 10 ай бұрын
I'm afraid I don't see it. The flexibility you have with a framework like moq would be very hard to achieve. You have to have the code for all possible uses of a method in the object you want to mock on a single interceptor, how are you going to know what are you mocking it for? If you could define one interceptor per test I could see it working, but that's not how that feature is going to work. Maybe with AsyncLocal you could pass some info or where this is coming from but, honestly, I don't even want to imagine the complexity of writing such a source generator, if it is at all possible.
@billy65bob
@billy65bob 10 ай бұрын
Thinking of Interceptors... Would it be possible to use it to it to intercept ALL property setters? Like for a MVVM viewmodel to perform its OnPropertyChanged notifications without all the boilerplate.
@logank.70
@logank.70 10 ай бұрын
Microsoft already kind of does this with their community toolkit package. It uses attributes and a source generator to get rid of that boiler plate you are referring too. It's a mixed bag in my opinion. I like the concept a lot but I wish they would've leaned more on convention instead of just attributes. Small things like "if the class has ViewModel at the end of the name...it is a view model so implement INotifyPropertyChanged and what not" or "if a private variable starts with p_ then it is a backing field for a property so generate the properties for it." I do like the idea of using an attribute for computed fields though. Something like [ComputedFrom(nameof(PropertyA), nameof(PropertyB)] comes to mind. One thing I do like is how they use [RelayCommand] as a way to source generate your ICommand properties. If it did a combination of convention and a couple well-placed attributes I think it would've been a lot better, but it is still much nicer than writing that boiler plate.
@billy65bob
@billy65bob 10 ай бұрын
@@logank.70 Interesting. I was aware of the RelayCommandAttribute, but not the weird logic for 'p_' names.
@logank.70
@logank.70 10 ай бұрын
@billy65bob they didn't go that route unfortunately. I just wish they had done things like that.
@phizc
@phizc 10 ай бұрын
@@logank.70 I've never seen p_whatever as a convention for backing fields for properties.. But CommunityToolkit.Mvvm is open source, so you could take their source generator code and adapt it to your preferences if you want. For me, I'm happy with the attributes.
@NickBullCSharp
@NickBullCSharp 9 ай бұрын
I'm not sure how I feel about this to be honest. But isn't that always the case when a new way of doing something comes out 😅. It feel wrong that the mocked file (source generated file) needs to be in the main project, when it's seems to only be used by the test project. Also, one reason to mock out a dependency, is that you then don't end up having to create multipe versions of a class for different scenarios. This feels like it could become that, if you have tests that check for different scenarios. In the video you just editted the MockGetSlugAsync method. But that would break your other test. On the plus side, having worked with bigs projects that have legacy code, that has then had to be re-written/re-architectured, this would save a lot of time and allow us to get tests in quicker. Maybe after they're in, then you could move to interfaces and mocking out. I'll have to watch your other video on interceptors and look into this more. Watch this comment become severely dated in a few months/years 🤪
@HimmDawg
@HimmDawg 10 ай бұрын
As long as we don't have to write the interceptors ourselves, hey why not. This would actually be useful in a legacy project I am currently working on. There's a lot of untestable stuff and transforming the codebase is just not feasable anymore at this point.
@nickst0ne
@nickst0ne 10 ай бұрын
I used to work on a legacy solution (comprising 90 projects, mind you!) that had been created with no unit tests and no interfaces. There was an integration tests base class, but it was a little hell to deal with and introducing unit tests was a lost cause. Even my senior colleague with 15 years experience gave up on it. Being able to introduce unit tests for that project would be a life-changer for my ex-colleagues.
@akiwoo5205
@akiwoo5205 10 ай бұрын
I have been asked a couple of times to add unit tests to legacy codebases. The issue we had was not only making sure that every inline database call and dependent service was covered, but that every change was also covered in the future. In the end, the cost was too high for the business. And that was with Fakes as an option.
@therealantiarchitect
@therealantiarchitect 10 ай бұрын
Legacy code bases have a problem though. You'd have to upgrade them to .Net 8 first. 🤔
@michaelgehling6742
@michaelgehling6742 10 ай бұрын
This sounds great, but I see a weakness: I usually need tests most when I am working on the code, especially with TDD. But in that case the line numbers might constantly change, so I have to adapt the interceptors all the time. Did I got it wrong? Is there a good solution?
@nickchapsas
@nickchapsas 10 ай бұрын
The idea is that all this will be code/source generated so file location or line/characters don't matter because code will be writing that constantly
@okmarshall
@okmarshall 10 ай бұрын
@@nickchapsas I would go one further and say if you're writing any line numbers/characters manually then you're doing it wrong (and setting yourself and your team up for a world of pain!)
@michaelgehling6742
@michaelgehling6742 9 ай бұрын
@@nickchapsas So the code generator will keep track of the code locations I want to "mock"?
@nickchapsas
@nickchapsas 9 ай бұрын
@@michaelgehling6742 Correct
@lordicemaniac
@lordicemaniac 10 ай бұрын
last video about interceptor I thought to my self, this will be useless for me, now I can see the use in testing, I'm just not sure how useful it will be compared to standard mocking because it is done through extensions which are static, not sure if it will be possible to run tests with different mocks
@ghaf222
@ghaf222 8 ай бұрын
Personally I struggle to see how maintainable code will be that uses this feature. Surely if you add in a new line or change the length of a variable or function name you’ll have to work everything out all over again?
@1000percent1000
@1000percent1000 8 ай бұрын
the doug demuro thing was gold lmao
@DemoricTv
@DemoricTv 10 ай бұрын
One thing i dont understand, how ill you create an interceptor for a method that needs to return something different between tests? In your example you comment out the previous code. But now the previous test would break. Can you have multiple interceptors for the same line?
@GeriatricMillenial
@GeriatricMillenial 10 ай бұрын
He mentions that issue. From what I am understanding, you would need to have some sourcegen code that detects the test change and compiles the change on the fly.
@sheveksmath702
@sheveksmath702 10 ай бұрын
Although I agree with many of the critiques here, I could see this being useful when mocking third-party code you don't control. I ran into a situation once where I was using a concrete class from the Twilio SDK that didn't have an interface, so I couldn't mock it. If interceptors existed, I could've mocked it just fine. On that note, I'd be curious as to how deep this can go. Could I intercept some internal code in a third-party library, forcing it to do exactly what I want?
@qj0n
@qj0n 10 ай бұрын
It looks like we kind of reinvented Shims from MS Fakes... Been there, don't go there ;) But to get to details - we need to change a built to make those tests working. You probably don't want to affect production builds, so there are two options: 1. Build two dlls, one for tests another for running(like MyLib.dll and MyLib.Testable.dll) plus some magic to switch them in tests 2. Add interceptors to Debug built, but not to release Then, we want one interception to handle multiple tests, so we intercept all calls and framework decides what mock code to run. At first glance it's doable if we run one test at time, otherwise hard to determine (especially if we intercept statics) The question is how deep dependencies should we intercept. If we test A, it uses B and B uses C and we want to intercept usage of C should we intercept calls inside B even though we are testing A? And what if B is in another project? And what if it's in nuget package? Are we expecting to see packages like *.Testable? (Just like we have Analyser packages for many nugets). I guess paackages should still use interfaces if it is useful for testing All in all, could be nice feature for legacy code bases, but when developing a testable solution, I'd not choose this as preferred way for testing
@tajkris
@tajkris 10 ай бұрын
I doubt this will change the way majority of people are writing tests and code. TypeMock and MS Fakes are there for very long time, allow for mocking pretty much anything without interfaces (by doing IL rewrites) but aren't that popular. Probably because of pricing, but even across big corporations which pay for VS Ultimate or have budget for TypeMock you still rarely see them used. And there are free, open source libraries with similar capabilities. So yeah, if you didn't want to write interfaces just for testing, you could do it even now and interceptors aren't going to change it.
@qj0n
@qj0n 10 ай бұрын
I used to work with Ms Fakes years ago and it was horrible. Shims sometimes just didn't work (good luck debugging it), build time was very long and we even had issues with our build agents out of memory as this generator was very heavy
@codingbloke
@codingbloke 10 ай бұрын
This could be very useful to create unit tests of legacy code that doesn't use DI. I wonder placing generated interceptors in generated namespaces and then using for that namespace in the text class file that needs it. Would that allow the separation of interceptors to respond with this different results, as needed by tests? hmm.. not sure.
@srivathsaharishvenk
@srivathsaharishvenk 10 ай бұрын
well, I am not sure we should be chaning anything in the main project. and this is not much different to mocking as the mock libraries does this behind the scenes using similar concepts to interception like proxies, etc. But I agree with not requiring interfaces all over the place. this is what TDD/component tests encourage, the contract we really care about is the one the system has with the external players, like API callers, Queues, DBs, downstream services, files, logs, etc. internally it does not matter if we have interfaces are not, thinking this ways will make the so called "unit tests" obsolete, the high-level tests with the TestWebApplicationFactory is all we need, then we can have an in-memory represntation of those external players using mocks or fake implementations, it does not matter as long as we are only intercepting in the tests and only the external points of the system
@radekzahradnik65
@radekzahradnik65 9 ай бұрын
Dear @nickchapsas, if you think that interfaces are most abused feature in C# (or it is for the fact your biggest problem), I totally envy you. Please, hire me for such projects! I would love to have these kinds of problems. What I see on not-just-legacy codebases is that developers with 10+ years of experience in C# does not even know how DI works, or how to use interfaces generally. IMHO: A feature with the highest abusing factor in C# are exceptions - they are just used as `goto`s.
@kocot.
@kocot. 9 ай бұрын
funny, I see the opposite pattern, people assuming the exceptions are just going to get handled somewhere :D and I always considered C# an escape from the java's exception fetish, where you'd pre-declare what exceptions a method might throw.
@matthias916
@matthias916 10 ай бұрын
cant you mock concrete classes using moq (or other mocking libraries) already?
@qj0n
@qj0n 10 ай бұрын
You can do it with Shims in Fakes, but this is old technology, actually similar to this in principles (generates code on build, allowing to modify particular methods in runtime). It didn't work well ;)
@kocot.
@kocot. 9 ай бұрын
you can and it's even a recommended approach for Azure SDK, the method simply need's to be virtual, not a big deal
@qj0n
@qj0n 9 ай бұрын
@@kocot. ...unless you're doomed to relay on some poorly written library and you want to mock it. But then this feature from video won't help you either
@sergiik2168
@sergiik2168 9 ай бұрын
Wow, that is really great feature for testing! But I want to ask you to use relative paths instead of absolute paths in your videos. It may confusing someone, especially newcomers, who are learning C# And they may wrongly thinking that it is ok to have absolute paths in code, which is obviously not ok.
@dwhxyz
@dwhxyz 10 ай бұрын
I wonder how many people remember Pex and Moles which allowed mocking of things which did not have interfaces. I also believe the product Typemock will also do the same but I've never tried it.
@dwhxyz
@dwhxyz 10 ай бұрын
Also there is very interesting NDC video "Hacking C# from the inside". This video demonstrates how to do dynamically intercept running C# to change what is actually executed. An extremely interesting video and well worth a watch.
@nickchapsas
@nickchapsas 10 ай бұрын
I have never heard of any of this, I really need to look into it
@JesperH
@JesperH 10 ай бұрын
As I understand, only one interceptor may point to the same file,line,char… having two would give you a compile error… Another problem I see here is that the interceptor will always be overruling the method and thus it will affect the actual binary… so whenever the method is called it will return null!
@rollingc2013
@rollingc2013 8 ай бұрын
This is like using a rocket launcher to kill a mosquito. A better example I think is to use of Func or Actionn to inject behaviour, that can be a better alternative than passing in interfaces.
@heyyrudyy404
@heyyrudyy404 8 ай бұрын
This looks like event-driven testing capabilities. The process of testing emits event and other part of code intercept event and handle it.
@desertfoxmb
@desertfoxmb 10 ай бұрын
As long as it doesn't become a crutch that enables bad design rather than going with TDD to help enable maintainable codebases. It seems like this could be abused (so can interfaces and mocks) if we're still thinking about testing _after_ writing the code like in the example in the video. I worry about long term maintenance and complexity. I definitely see applicability for third party classes that do not expose an interface/abstraction for easy testability; we should still use this in addition to still using interfaces where interfaces are actually more appropriate. In other words, still no silver bullet.
@lucasmicheleto2722
@lucasmicheleto2722 10 ай бұрын
Hello Nick, do you know what "gambiarra" means?
@piotrus5457
@piotrus5457 9 ай бұрын
Don't you think absolute paths in code are ok? After all, as if we were working together and this code I do not have the same path to the file as you.
@brooklyndev
@brooklyndev 10 ай бұрын
When I heard of the new interceptors feature, this was my first thought as well - "will be great for testing". Previously, the only way to mock or stub out something that was either non virtual or static was to use Microsoft Fakes (using Shims) but that's only available to VS Enterprise users. (There are some other alternatives, but nothing free as far as I can tell). This will certainly change the way we do testing, win win.
@ronsijm
@ronsijm 10 ай бұрын
The alternative is to just wrap your class in a castle dynamic proxy object, and inject an interceptor in there. Seems a lot easier as well, since you don't have to intercept on a invocation / per line level, but just on method level
@JamesOfKS
@JamesOfKS 9 ай бұрын
@@ronsijm afaik you cannot inject an interceptor on a static method. and one nasty situation is extension methods. An extension method cannot be part of 'the interface' because then it wouldn't compile so inherently cannot be mocked but they modify state of the object they extend so they can be a place for untestable dependencies. Interceptors seem like @brooklyndev pointed out the use of fakes/shims but requires vs enterprise.
@garcipat
@garcipat 8 ай бұрын
This does only work for only one usecase. Seems also weird to debug or follow it since its jumping around.
@kejansenz
@kejansenz 10 ай бұрын
Unfortunately I don't agree with using interceptors. Various other people in the comment section pointed out major drawbacks. I want to add that testing like this hampers find all references(the unit tests using the code) during minor refactoring, makes it difficult to do unit test code coverage reports. Also how is quality control software like SonarQube going to deal with this?
@futurexjam2
@futurexjam2 10 ай бұрын
yep, maybe if we do not need virtual extends or contract management, interceptor maybe useful for similar cases such as testing. Otherwise, even mappers kill readability, interceptor will have a nuke effect.
@jchandra74
@jchandra74 10 ай бұрын
Hmm... What about if the thing that you are intercepting supposed to return different thing on a different test?
@talwald1680
@talwald1680 10 ай бұрын
This is very similar to how mocking functions works in python - and is really hard to maintain or understand the test. This normally shows a problem with the design more than an issue with the testing framework from my experience.
@ImmacHn
@ImmacHn 9 ай бұрын
So do we need a Carrier then?
@Termit2009
@Termit2009 9 ай бұрын
In general the idea is very cool, but I suppose it will not be implemented at the nearest future as we'll need to sacrifice the "on-the-fly re-compilation" feature during tests debugging
@jameshancock
@jameshancock 10 ай бұрын
Still fail to see the point in mocking database interactions. 99/100 the reason your code will fail is the database. Yet we write unit tests that eliminate the database from testing. I get the example, but it should be pointed out, that if your unit tests don't test your database, then you're still not (really) testing.
@Bliss467
@Bliss467 10 ай бұрын
I expect rider, visual studio, or Roslyn will probably get a validator for if these are mapping correctly, especially if an agreed upon nomenclature arises
@sizenineelm
@sizenineelm 7 ай бұрын
Why would you use an interceptor for testing when an interface with mocking already solves the problem with a well-recognised pattern? Individual tests can set up the calls/results without needing multiple interceptors defined for the same calls. This appears to be a solution trying to solve the wrong problem.
@dkostasx
@dkostasx 10 ай бұрын
This could be really useful when working with some old code which because of its complexity cannot be easily refactored
@rafae5902
@rafae5902 10 ай бұрын
Yeah, that's the only case where I think this new feature should be used for mocking. Legacy code that is too costly to make testable and ideally isn't being changed often.
@ryanseipp6944
@ryanseipp6944 10 ай бұрын
Nick you said the purpose of these is AOT. Now I'm normally all about doing things at compile time, but i just dont see why tests need to be AOT compiled, when you really just want to run tests quickly to get feedback. Why not stick to reflection or find a way to source generate the implementation to an interface rather than additionally needing to statically analyze every call a class will take from one of the test methods, just to get those line/column numbers...
@phizc
@phizc 10 ай бұрын
It's not that interceptors are for AOT only. It's that interceptors (or something similar) is _required_ for some AOT scenarios since you can't do some things in AOT that you can do with JIT. Interceptors let's you do these things at compile time instead of at run time.
@ahmedalirefaey3219
@ahmedalirefaey3219 10 ай бұрын
i think it will make chaos in you legacy code base cause it suppose unit tests in another namespace and will affect i guess
@bondarenkodf
@bondarenkodf 9 ай бұрын
I would be happe to have this feature. It's hard to count how many things I've had to fix internally using Xamarin.Forms. So, instead of having the patched XF nuget package everything I need is to fix the wrong method. It would be nice to have access to private/protected/internal things too. It would be extreme cool to be able to combine this feature with the Source Link too. Just imagine you have some exception in the third-party library, you debug it using source link, then patch the method in 1-2-3 clicks!
@sr_meia_noite
@sr_meia_noite 8 ай бұрын
OOP programmers when they realize they don't need 99% of OOP concepts:
@officemishler3364
@officemishler3364 10 ай бұрын
I made it as far as providing the line number and character location before deciding this is worse than mocking.
@davew2040x
@davew2040x 10 ай бұрын
Oh please God, don’t tell me that Nick is jumping on the interceptor train
@christopherwilliams3293
@christopherwilliams3293 10 ай бұрын
I would not go this route, but I do wish C# had a good level of AOP support like Java.
@matt-irby
@matt-irby 9 ай бұрын
THIS contains an excellent Doug DeMuro reference 😄
@wknight8111
@wknight8111 10 ай бұрын
I'm not thrilled about Interceptors for test mocking purposes. It feels like too clunky of a system, at least until some serious work is put into doing a code-generating mock framework to create them. BUT then I have to ask what the benefit would be to using interceptors over existing proxy mechanisms? Or even Fody-style code weaving? It's hard for me to see how Interceptors either enable functionality we don't already have OR make improvements over existing functionality (like perf improvements, etc).
@Exosia
@Exosia 10 ай бұрын
I'm really not convinced by this interceptor. Also, I'm not a fan of moving code from one file to another to do almost the same thing. I prefer to centralize my test and the "Arrange" part (setup) in the same place than to separate everything.
@rvrunkillyow716
@rvrunkillyow716 8 ай бұрын
it is pretty cool feature.❤
@z0nx
@z0nx 10 ай бұрын
You suggested removing the interface from the one thing that actually makes sense to be behind an interface. And instead completely missed out on the fact that those IValidators should be the ones to rip out of the code. Validation should just be a pure function and shouldnt be hidden behind an interface?
@robsosno
@robsosno 10 ай бұрын
In corporate world it would be beneficial to have possibility to mock static classes and methods. This is because of "quality gates" which require certain level of code coverage. Because of this people are avoiding static classes. But these interceptors will not help. They require to give exact location in code which should be intercepted. The problem is that different test environments require different path.
@JackBauerDev
@JackBauerDev 10 ай бұрын
Mock frameworks still have a lot more functionality, just for example, verify.
@proosee
@proosee 10 ай бұрын
This is the wrong way in my opinion, we should have like special operator called, for example, "interfaceof", so we can extract an interface of a class if it covers all non-static public properties/method. It's cleaner, simpler, less verbose and the most important: intuitive. That would not only simplify mocking of our own services, but most importantly we will be able to abstract from third party services which authors didn't thought about exposing interfaces (today we need some libraries that do assembly magic at runtime).
@Krimog
@Krimog 10 ай бұрын
There's also another problem with your code: if you actually run it, the interceptors will still be called instead of the actual code...
@benomine
@benomine 10 ай бұрын
Because you want to generate the code only during test and not for the actual debug/release and get rid of it afterward, that's why Nick told us to assume the code is generated.
@Krimog
@Krimog 10 ай бұрын
@@benomine The problem is that interceptor methods have to be accessible by the intercepted code. So basically, the generated code has to be in your "non-test" dll/exe, whether it's called by your test code or not.
@AnatholyBonder
@AnatholyBonder 10 ай бұрын
And of course it supposed to be gitignored because of absolute path... So it seems this have to be generated just before test run and deleted after the test done.
@dariogriffo
@dariogriffo 10 ай бұрын
Just use interfaces to mock IO abstractions. Use classes if you need to keep track of changes during the lifetime of the object and use statics methods in any other case.
@Fred-yq3fs
@Fred-yq3fs 10 ай бұрын
If a branch depends on the return of a static, then your test setup can get very obscure only to be able to cover that specific branch. Combine static with statics, have nested branches, and see your tests exploding. You need to decouple. If you have mostly flat code then using statics is fine, but soon enough branching will crop up.
@dariogriffo
@dariogriffo 10 ай бұрын
@@Fred-yq3fs if your code has statics and nested branches and more statics then you need to refactor it. Have you ever wondered how the Linux kernel works without injecting a single interface? If the most used system ever built can live without them in sure your API with a call to the dB and return an Ok can live without 236556483338847474 interfaces
@Eddyi0202
@Eddyi0202 10 ай бұрын
@dariogriffo Do you have some article to point out that shows an example of such approach using static methods?
@VladimirCheTV
@VladimirCheTV 10 ай бұрын
Unexpected Doug :)
@ncvjr
@ncvjr 10 ай бұрын
Probably the first time that I not agree your approach. It’s weird. The tracking of mock is a phantom in the code. :/
@AndriyBezkorovayny
@AndriyBezkorovayny 10 ай бұрын
Thank you for explaining this new feature, but IMHO "old-school" mocking is less confusing.
@neociber24
@neociber24 10 ай бұрын
This is just a building block, You are not suposse to test this way.
@MrSamisack
@MrSamisack 10 ай бұрын
felices san fermines mi gente
@modernkennnern
@modernkennnern 10 ай бұрын
I love interceptors. I understand many people's worries, but I think it's generally overblown. IDEs will, similar to implicit conversions, give you intellisense whenever they're intercepted.
@haxi52
@haxi52 10 ай бұрын
This will be tricky since the interceptor is declared once per assembly. Means the interception logic has to be shared amongst all the unit tests in the same project.
@Fred-yq3fs
@Fred-yq3fs 10 ай бұрын
Yes: If used for mocking then 1 method must be able to be intercepted by n interceptors. That leaves the problem of knowing which one to use, meaning an interceptor should exist in some sort of context that would make it unique. If interceptors stay as they are then they could be used as a life-saver, in legacy code where refactor to inject is not a viable option, but that would not be pretty.
@StephenZura
@StephenZura 10 ай бұрын
If it requires a static path that will never change, that's already a failure point. I have yet to meet a team of developers that use the exact same pathing across their systems to store their code. On top of that, Line and column definition seems incredibly clunky and prone to breaking.
@phizc
@phizc 10 ай бұрын
You're not supposed to write the interceptors, or at least that attribute yourself. You're supposed to use source generators, and they'll update the path/line/column if anything changes.
@janhendrikschreier
@janhendrikschreier 10 ай бұрын
Around the 7 mark I had a brief Fight Club flashback
@the_wilferine
@the_wilferine 10 ай бұрын
That Doug DeMuro reference though :D
@jeroen7362
@jeroen7362 4 ай бұрын
Yeah, i may be an outcast for my opinion but there are a few guides i have for tests. when they are more complicated then your code they are making things worse. they would need a test for the test! A test should really test something and not just hit mocks. testing crud without any validators in it, is just testing if your database works but then without using a database? you test nothing! But if you do have a validator you have a good case to write a test, make sure in your design that you can call that validator without using mocks, a validator should need no database, i would be just a bunch of logic, like not null and not longer then, not containing invalid stuff etc. If it needs data from a location then just give that data as parameter so it could be a static validator that is something you probably can test without the need of mocks. So write tests to actually test something and not to hit a line of code just for the metrics
@MrErnow
@MrErnow 10 ай бұрын
I am a strong advocate of testing the binary that will be deployed, not code that is 'different' through weaving. The most pragmatic way forward is having integration tests of the public API/interfaces. Internal classes, DTOs etc. count towards the code coverage of the integration tests. Unit testing every single class, every single class being mockable/having an interface is a pain with refactoring.
@paulkoopmans4620
@paulkoopmans4620 10 ай бұрын
Same here. I totally agree! There is several reasons why the binary will not be able to be weaved in the first place. For example I have part of the total code base being its own project with tests and results in a private nuget package. Other parts of our code base will use the nuget package. This type of waiving would simply not work. I also agree with you and Nick that not everything needs to be an interface and they are overused. Maybe one of the reasons is that the 'unit' of unit testing is not necessarily 'clear'? Maybe because examples and blog posts always focus on a small class. If one would write a custom version of some list, stack or other collection, these ideally should be unit tested, where the unit is the thing itself. Then for the rest there is exactly what you mentioned; unit tests on larger scale behaviour all the way up to basically calling the Api method. - Martin Fowler calls it solitary vs. sociable. - Dennis Doomen's 17 laws of TDD; * from 2: " accept that you need tests on different levels (e.g. class, component, module, API, UI)" * from 3: "Choose the right scope for your tests." and "a test-per-class is almost always the wrong approach" * from 16: "Avoid overuse of mocking libraries." Even if I imagine a package that would do this, I struggle to see how a single intercept would do multiple different things, like succeed and return different things for a couple of tests but also expectedly fail for another. And I think that parallel tests might be an issue too.
@diadetediotedio6918
@diadetediotedio6918 10 ай бұрын
> Just don't use
@pilotboba
@pilotboba 10 ай бұрын
You can of course mock classes, assuming the methods you want to mock are marked virtual. Perhaps better than making a god damn interface for every god damn class. :)
@AaronShumaker
@AaronShumaker 8 ай бұрын
I've always disliked interfaces used just to support unit testing and DI. It's not what where their value is, and is only used there as a crutch for lack of a better mechanism. If your interfaces always have exactly one runtime implementation, then you're not really using them where they are beneficial. This makes it really hard to recognize when an architecture is using an interface for more than just to support DI, and it becomes obtrusive to decompose types into proper interfaces. Juval Lowy has some old talks on decomposing interfaces that I suggest everyone watch.
@Thorarin
@Thorarin 10 ай бұрын
So, basically analternative to Microsoft Fakes? I think that uses profiling APIs to intercept calls. Having this capability without the Enterprise license pay wall might be nice 🙂
@djupstaten2328
@djupstaten2328 9 ай бұрын
Just give us more evolved constraints, such as the C++ 'Concepts', exposing possibilities by removing possibilities, being able to funnel down into functionalities that become inferable by the compiler.
@alexanderzolotaryov7092
@alexanderzolotaryov7092 9 ай бұрын
that DeMuro cameo made me laugh tho
@elpe21
@elpe21 10 ай бұрын
Am I right in thinking that the test will fail when someone visit the class and decide to add a comment line as the interceptor won't be executed properly ?
@phizc
@phizc 10 ай бұрын
No. You're not supposed to write that attribute yourself. A source generator should be used to write the interceptor for you.
@brianviktor8212
@brianviktor8212 10 ай бұрын
Your Complaint object about interfaces has been noted as an IComplaint. No, but interfaces can be really nice, useful and clear to understand. As everything they can be overused or badly used, but Interceptors won't change that.
Collections Just Changed in C# 12 and That’s Good
8:01
Nick Chapsas
Рет қаралды 103 М.
xUnit or NUnit? Picking the Right Testing Library
10:00
Nick Chapsas
Рет қаралды 44 М.
Süper ❤️ Cute 💕💃 #dance
00:13
Koray Zeynep
Рет қаралды 22 МЛН
INO IS A KIND ALIEN😂
00:45
INO
Рет қаралды 12 МЛН
How Senior Programmers ACTUALLY Write Code
13:37
Healthy Software Developer
Рет қаралды 1,2 МЛН
Spring Boot Microservice Mini Banking App Part 1 - Setup
10:57
Chukwunonso Izueke
Рет қаралды 6
Every single feature added in C# 11
27:07
Nick Chapsas
Рет қаралды 109 М.
The C# 12 Feature You Shouldn’t Use, Yet
10:07
Nick Chapsas
Рет қаралды 33 М.
The Best Way to Add Health Checks in Any .NET App
12:31
Nick Chapsas
Рет қаралды 84 М.
What are Interfaces? (C# Basics)
13:54
Code Monkey
Рет қаралды 197 М.
The Only .NET Scheduler You Should Be Using!
16:38
Nick Chapsas
Рет қаралды 24 М.
The High Performance Types You Ignored for Years in .NET
10:14
Nick Chapsas
Рет қаралды 44 М.
Testing WITHOUT Mocks or Interfaces!
12:27
CodeOpinion
Рет қаралды 23 М.
This VS Code AI Coding Assistant Is A Game Changer!
14:27
codeSTACKr
Рет қаралды 95 М.
Süper ❤️ Cute 💕💃 #dance
00:13
Koray Zeynep
Рет қаралды 22 МЛН