Testing WITHOUT Mocks or Interfaces!

  Рет қаралды 25,489

CodeOpinion

CodeOpinion

Күн бұрын

Пікірлер: 53
@distinguishedmoments2277
@distinguishedmoments2277 2 жыл бұрын
These videos are absolute gems in a world filled with rookie videos. Thanks brother
@CodeOpinion
@CodeOpinion 2 жыл бұрын
You're welcome.
@Tony-dp1rl
@Tony-dp1rl 2 жыл бұрын
Nice video. I think a lot of times developers get trapped into patterns, particularly OOP patterns, and forget how simple some things are with more functional approaches to Dependency Injection.
@CodeOpinion
@CodeOpinion 2 жыл бұрын
Method injection is also highly underrated and often frowned upon, when I don't think it should be at all.
@Knigh7z
@Knigh7z Жыл бұрын
Classes with static methods are just namespaces for functions. Definitely easy for people to be blinded by OOP.
@TheRicherthanyouguy
@TheRicherthanyouguy 2 жыл бұрын
Since the core concept of this video is discussing other ways to test a piece of code I think it is worth while to mention I’ve had success specifically in making legacy code bases testable or easier to test by using default input parameters. Usually adding a simple conditional to check the value with a default before doing some other bit of logic. It does still require code changes but the changes are usually very small and default values often can prevent the breaking of other preexisting tests. Again I other mention this as one of many options that have had value for me and they seem to align with Derek’s goal of the video so thought I’d share.
@jrhodes69
@jrhodes69 2 жыл бұрын
For comparing dates against utc now I think the tolerance approach is good enough if sub second accuracy isn’t required ( which is probably most cases). You can make the tolerance big enough so that it would never fail in a unit test as generally the time between the sut being exercised and the assertion being made is very small. Even if it isn’t, you can tweak the tolerance higher as long as it still makes sense in the use case. I’ve used the tolerance approach for a while and I find it a much better and cleaner solution than having to inject extra dependencies just for testing.
@CodeOpinion
@CodeOpinion 2 жыл бұрын
Date (UtcNow) was just the example of a non-deterministic dependency.
@nonamezz20
@nonamezz20 Жыл бұрын
What is the benefit of using the delegate over the interface for the date time class ?
@tomheijtink8688
@tomheijtink8688 2 жыл бұрын
It reminds me of a DI system I once made for personal practice. Before I knew about DDD my classes generally only had one public method and no state. But in some cases, I still needed to DI dependencies. So, my delegates didn't always match the signature of the public static method but instead resolved the dependencies via the service provider and registered an action or function which would essentially inject the dependencies into the methods. This worked really well. It was a bit more work to bootstrap. As at that time I was manually resolving the dependencies for some of the method arguments. But I liked the concept. This idea removes an interface and introduces a delegate. Which are actually the same, but one is on class level while a delegate is on function level. Which is a bit more to the point in his example. So, for the instances where you don't need a set of grouped methods but just only one. A delegate is a good option as it is more to the point and less code to write.
@CodeOpinion
@CodeOpinion 2 жыл бұрын
Delegates are severely underused imo. Especially if you have interfaces that their consumers ever only call one method.
@alexandre-pl
@alexandre-pl Жыл бұрын
The simplest approach to testing is to focus on having pure domain code (free of out-of-process dependencies) which eliminates the need for mocking and then test the application layer through integration tests. The example you shown where the DateTime was provided as a method argument shows how to eliminate out-of-process dependencies, but it wouldn't make sense to have such method contract at the application layer level, where the client is expected to provide inputs.
@CodeOpinion
@CodeOpinion Жыл бұрын
Yes, as you move outward from the core you're going to have to provide dependencies that likely have side-effects.
@marcozg77
@marcozg77 Жыл бұрын
Thanks for your video and I find it very interesting to see people who are questioning mainstream practices. I'm working as a sw engineer since 1999 and have seen a nice shift from dogmatic to pragmatic in all the years which I personally find liberating (e.g. shifting from "everything is an object because OOP is the only way to go" to "use whatever programming paradigm fits the problem best"). I agree that you don't need an interface for everything. I'm not quite sure whether the first example is good design though: now you expose an implementation detail of the method to the caller, essentially forcing him to decide what to pass. How does he know which datetime value to pass? I don't say it's wrong but it changes the semantics because before, it was part of the method to decide which datetime is used while afterwards, it's up to the caller. For me, this looks like something that isn't really something that is that useful to unit test. We have a lot of such methods that are basically just wrapping some insert action, maybe adding some simple transformations. We simply test them "manually" by invoking the respective ui action (for example). Ideally, all the real logic (e.g. calculations etc.) is into separate, side-effect free classes, that can easily be unit tested. IMHO testing against a method that doesn't do much more than inserting into a repo, by mocking the repo, doesn't really add any value other than to testing that your mock lib is working. We do use interfaces for our repos but usually not for mocking reasons; we try to separate the real logic into side-effect ut-testable classes and test the bare crud methods manually.
@Greenthum6
@Greenthum6 2 жыл бұрын
I like to follow KISS principle and just use interfaces and stubs in test code. This makes code easily readable for everyone. Mixing delegates into constructors achieves little and adds complexity. I think mock code is generally unreadable and use it usually only in cases where the dependency is irrelevant. Testing against DateTime.UtcNow is a systemwide challenge, and method parameter injection is bad idea as it needs to be in several places. Again, interface with DI works well. HttpClient is IDisposable so injecting it via constructor should be ideally replaced with .NET best practices instead. If there is HttpClient involved an integration test is usually better choice. Overall, I like any videos about test automation. No need to nitpick, but hopefully this gives ideas and maybe prove me wrong. Ofc some things are just matter of taste:) I would like to see a video about more realistic app integration testing where these ideas are shown.
@CodeOpinion
@CodeOpinion 2 жыл бұрын
"Mixing delegates into constructors achieves little and adds complexity." "Again, interface with DI works well." We will have to to agree to disagree on this one. 😆 Not sure how injecting a delegate (a function) into a class ctor adds complexity over injecting an interface? About method injection, I'm going to assume you don't like the method injection in ASP.NET Core?
@Greenthum6
@Greenthum6 2 жыл бұрын
@CodeOpinion By added complexity, I mean using two different approaches to enable testing via constructor. If there is a coding convention, then which one to use depending on the case? Does it make writing and reading tests easier? Method DI injection works for pure functions, although not a fan. In the video example, passing DateTimeOffset requires the coder to understand what is its purpose. If it's custom delegate, then using it in production code needs always to understand to pass now. It also exposes the method for incorrect creative use. I use ITimeProvider interface to provide time everywhere and register impl in DI. Then it is easy to create and test different time patterns like hardcoded, interval stepping or time variations.
@EMWMIKE
@EMWMIKE Жыл бұрын
httpclient shouldnt be disposed. if you have a high volume flow like creating orders, it will open op new ports and not close them for a couple of minutes. Making you get socket exhaustion
@notavailable4891
@notavailable4891 Жыл бұрын
The problem I have with delegates is it seems similar to not using curly braces on a one line if or immediately returning the result of a function called within another function. In theory, it's nice to keep the code concise but almost inevitably, you need to add code to the if block so now you need to add the curly braces, or you want to debug the result of the function called within the function before it returns or manipulate it in some way. The delegate seems like it will often need to be refactored into a class except unlike my example it will be a pretty big deal if/when it does because you not only need to create the class and interface and the DI for that but then you need to rewrite the test. Idk just sounds like the kinda pain I try to avoid.
@martijnveld1053
@martijnveld1053 2 жыл бұрын
Instead of having to create a Mock HttpMessageHandler and do a lot of plumbing yourself in your tests, you can also use the HttpTest class that comes with the excellent Flurl package. Yes it is another set of dependencies to add, but it greatly simplifies the way tests interacting with external services can be set up.
@CodeOpinion
@CodeOpinion 2 жыл бұрын
Thanks for the suggestion!
@RasmusSchultz
@RasmusSchultz 2 жыл бұрын
This is fine, but always leaves me longing for language features that make this more easy and natural. Delegates are interface types (you just covered this in a previous video) so there isn't much of a difference - you're still abstracting and mocking. With structural type checking in Typescript, you can substitute anything anywhere, without introducing abstractions. Same in Go. In Dart, you can type-hint using class names, which makes them double as implicit interfaces. I feel like these features have testing in mind. I wish C# would do some reckoning and figure out how to make the language more naturally lend itself to testing without so much plumbing and ceremony - without generating so many artifacts. 🤔
@CodeOpinion
@CodeOpinion 2 жыл бұрын
Agreed.
@buriedstpatrick2294
@buriedstpatrick2294 2 жыл бұрын
I had a discussion with a colleague who really likes using mocks for services I personally would consider very bloated. I decided to read a bit about some opinions on mocking online and came across the argument that using mocks should really just signal that a specific unit test should be an integration test instead. The more I thought about that argument, the more it made sense to me. I realize this is not always feasible to implement across the board, and sometimes we need to cut corners, but it also made me realize how many of our unit tests aren't really true black box tests. We check that internal calls are being made to mock services, completely violating the encapsulation principle of the SUT. Furthermore, changes to the DI hierarchy can break these tests in ways that aren't immediately obvious until you run the test and get a DI-error that isn't relevant to what you're really testing. Whereas if you change a method signature, you'll know immediately as it won't compile until you fix the test. Additionally, if your method requires some external injected dependency to behave in a specific way, maybe the method is doing too much. Why not break it into helper methods that can be individually unit-tested and then run the full method as an integration test? You can even make them static, which makes them the simplest unit to test. Anyways, I'm curious on your opinions on it. I know you're not anti-mock, and I wouldn't exactly call myself that either. But it does come off as a crutch to me.
@CesarDemi81
@CesarDemi81 2 жыл бұрын
Unit tests are not black boxes. Unit tests are meant to cover every execution path of the method/function (unit) being tested, so because you need to cover those paths to have proper coverage, you HAVE to know what's inside of them, thus not working under the black box concept.
@mylesdavies9476
@mylesdavies9476 2 жыл бұрын
This channel is so underrated, great content, thanks
@CodeOpinion
@CodeOpinion 2 жыл бұрын
Glad you think so!
@allinvanguard
@allinvanguard 2 жыл бұрын
Injecting funcs is a good one. I guess in general, when it comes to what is allowed in testing and mocking, it can be summed up as: Don't change your code in order to make tests works, change your tests in order to make them work with your code. So it's fair game as long as it is not invasive.
@CodeOpinion
@CodeOpinion 2 жыл бұрын
I'm not so strict on the "Don't change your code in order to make tests works". But I think if something is difficult to test, that might be a sign.
@thelonearchitect
@thelonearchitect 2 жыл бұрын
I'd add : develop in a testable way. And start with TDD to do so.
@kristianaranda
@kristianaranda 2 жыл бұрын
Yes, I agree. it's a bit of code pollution. In my humble opinion, the static field could introduce a shared dependency between tests as well.
@MrNuneD
@MrNuneD 2 жыл бұрын
Great video, just shows there is nothing better than simplicity. If using mocks I recommend the library FakeItEasy instead of Moq. Easier to read, setup and cleaner code on the tests.
@CodeOpinion
@CodeOpinion 2 жыл бұрын
Yes, I've used FakeItEasy in the past. Good suggestion.
@aneeszuberi1690
@aneeszuberi1690 Жыл бұрын
I am totally now to testing. this is a great video even I was not able to learn much. I need you guidance, I am working on a project in which I need to create an interface but I have no idea what it is, how to do it etc. Can you guide me where to start and if you can attach a link, that would be great. Thanks
@dimitryk8429
@dimitryk8429 2 жыл бұрын
single approch with injected interfaces is better then mixing in delegates and still using interfaces for all other stuff
@airman122469
@airman122469 Жыл бұрын
The code I work on right now has dozens of internal interfaces for the purposes of “testing” which it totally insane to me. I hate it. I hate it so much. It’s definitely a design failure.
@chriscrossx
@chriscrossx 2 жыл бұрын
Thanks for the great content! Python has an awesome library called "freezegun" which hooks into system time calls and mocks the date for the whole process. You can essentially freeze the time for the duration of the test or part of the test. Basically a system level mock. We've found it preferable to passing in date arguments which tends to get tedious when you have to pass it all the way down a call stack. I'd be curious to know if you think relying on this kind of system level mocking is a good way to go.
@CodeOpinion
@CodeOpinion 2 жыл бұрын
Interesting. I'm not immediately against the idea. Ultimately it's about being deterministic as much as you can. Moving things like I/O and side effects up the call stack help with making lower/inner deterministic or as the cool functional kids say, "pure functions". As for a global mocking of a date... I can see this working in some situations but not all.
@chriscrossx
@chriscrossx 2 жыл бұрын
@@CodeOpinion yeah. There seems to be something special about "now" date handling that makes it less scary to mock in this way. I think you are right to prefer/suggest dependency injection where possible 👍
@Rookiande
@Rookiande 2 жыл бұрын
Shouldn't be the title about using manually created fakes/test doubles (mocks/stubs) vs library-created ones?
@CodeOpinion
@CodeOpinion 2 жыл бұрын
If that's the case I didn't get the point across well enough. Fair enough though.
@FISS007
@FISS007 2 жыл бұрын
The main takeaway is: Use whatever works, is clean and saves you time =) Thanks for sharing.
@oscareriksson9414
@oscareriksson9414 2 жыл бұрын
Cool! Alternatives are good.
@jeffrdrama7984
@jeffrdrama7984 2 жыл бұрын
How do I know if this guy knows what he's talking about?
@CodeOpinion
@CodeOpinion 2 жыл бұрын
You don't😆
@parlor3115
@parlor3115 2 жыл бұрын
I'm not sure if this is actually what the video is advocating but why not just isolate all the business logic into pure functions? If you have to load data from the database or from a network call, do it in a topmost function (which is obviously not pure), but then have all that loaded data pass through a series of functions that are pure which transform the initial data or generate new data based on it. Finally, take the resulting data and persist it to the database or send it over the network and that's it. Now you have a perfectly testable set of functions. It's as easy as that I guess.
@CodeOpinion
@CodeOpinion 2 жыл бұрын
That's pretty much what I leading up to when I was passing in the date to the create order.
@eltyo340
@eltyo340 2 жыл бұрын
@@CodeOpinion Any F# videos coming up? :p
@parlor3115
@parlor3115 2 жыл бұрын
@@CodeOpinion Cool, hopefully more devs start to write code like this. It'll make everyone's lives easier as the code is easier to reason about and ofc easily testable.
@sandromagalli8587
@sandromagalli8587 2 жыл бұрын
Sorry im new to programming, can you give me a practical example about passing functions? Do you mean like having a service with a method to save entities like: public void SaveEntity(Guid entityId, TSource source, Func getEntity, Action updateEntity) { var dbContext = _dbContextFactory.Make(); var entity = getEntity(dbContext, entityId); if(entity is null) { entity = new (); dbContext.Add(entity); } else { updateEntity(entity, source); } dbContext.SaveChanges(); } And all the funcs passed as arguments will be in a static class so you call _databaseService.SaveEntity(id, source, DbFuncs.GetEmployee, DbFuncs.UpdateEmployee);
@badderborroda4972
@badderborroda4972 Жыл бұрын
All of these examples are just testing things you've explicitly created in the test - they provide 0 value other than test coverage.
@georgehelyar
@georgehelyar 2 жыл бұрын
For HttpClient I just use Moq.Mock.Protected. Add a couple of extension methods for convenience and it's as easy to use as any other mock.
"I NEED data from another service!"...  Do you really?
10:41
CodeOpinion
Рет қаралды 32 М.
How to AVOID Mocking Frameworks
13:39
Gui Ferreira
Рет қаралды 2,3 М.
Молодой боец приземлил легенду!
01:02
МИНУС БАЛЛ
Рет қаралды 2,1 МЛН
How To Choose Mac N Cheese Date Night.. 🧀
00:58
Jojo Sim
Рет қаралды 98 МЛН
Кто круче, как думаешь?
00:44
МЯТНАЯ ФАНТА
Рет қаралды 6 МЛН
Creative Justice at the Checkout: Bananas and Eggs Showdown #shorts
00:18
Fabiosa Best Lifehacks
Рет қаралды 8 МЛН
You DON'T want an In-Memory Event Bus like MediatR
11:20
CodeOpinion
Рет қаралды 23 М.
Tired of Layers? Vertical Slice Architecture to the rescue!
12:26
i fell for mocks again...
9:08
TheVimeagen
Рет қаралды 28 М.
Thoughts About Unit Testing | Prime Reacts
11:21
ThePrimeTime
Рет қаралды 234 М.
Don't Mock 3rd Party Code
19:56
Continuous Delivery
Рет қаралды 40 М.
C++ testing with mocks is EASY and awesome!
10:00
Smok Code
Рет қаралды 19 М.
This Will Make Everyone Understand Golang Interfaces
21:03
Anthony GG
Рет қаралды 55 М.
When To Unit, E2E, And Integration Test
14:58
ThePrimeTime
Рет қаралды 102 М.
Stop Using Mocking in Unit Testing! | Code Cop #020
10:46
Nick Chapsas
Рет қаралды 48 М.
Молодой боец приземлил легенду!
01:02
МИНУС БАЛЛ
Рет қаралды 2,1 МЛН