These videos are absolute gems in a world filled with rookie videos. Thanks brother
@CodeOpinion2 жыл бұрын
You're welcome.
@Tony-dp1rl2 жыл бұрын
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.
@CodeOpinion2 жыл бұрын
Method injection is also highly underrated and often frowned upon, when I don't think it should be at all.
@Knigh7z Жыл бұрын
Classes with static methods are just namespaces for functions. Definitely easy for people to be blinded by OOP.
@TheRicherthanyouguy2 жыл бұрын
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.
@jrhodes692 жыл бұрын
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.
@CodeOpinion2 жыл бұрын
Date (UtcNow) was just the example of a non-deterministic dependency.
@nonamezz20 Жыл бұрын
What is the benefit of using the delegate over the interface for the date time class ?
@tomheijtink86882 жыл бұрын
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.
@CodeOpinion2 жыл бұрын
Delegates are severely underused imo. Especially if you have interfaces that their consumers ever only call one method.
@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 Жыл бұрын
Yes, as you move outward from the core you're going to have to provide dependencies that likely have side-effects.
@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.
@Greenthum62 жыл бұрын
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.
@CodeOpinion2 жыл бұрын
"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?
@Greenthum62 жыл бұрын
@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 Жыл бұрын
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 Жыл бұрын
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.
@martijnveld10532 жыл бұрын
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.
@CodeOpinion2 жыл бұрын
Thanks for the suggestion!
@RasmusSchultz2 жыл бұрын
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. 🤔
@CodeOpinion2 жыл бұрын
Agreed.
@buriedstpatrick22942 жыл бұрын
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.
@CesarDemi812 жыл бұрын
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.
@mylesdavies94762 жыл бұрын
This channel is so underrated, great content, thanks
@CodeOpinion2 жыл бұрын
Glad you think so!
@allinvanguard2 жыл бұрын
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.
@CodeOpinion2 жыл бұрын
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.
@thelonearchitect2 жыл бұрын
I'd add : develop in a testable way. And start with TDD to do so.
@kristianaranda2 жыл бұрын
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.
@MrNuneD2 жыл бұрын
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.
@CodeOpinion2 жыл бұрын
Yes, I've used FakeItEasy in the past. Good suggestion.
@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
@dimitryk84292 жыл бұрын
single approch with injected interfaces is better then mixing in delegates and still using interfaces for all other stuff
@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.
@chriscrossx2 жыл бұрын
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.
@CodeOpinion2 жыл бұрын
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.
@chriscrossx2 жыл бұрын
@@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 👍
@Rookiande2 жыл бұрын
Shouldn't be the title about using manually created fakes/test doubles (mocks/stubs) vs library-created ones?
@CodeOpinion2 жыл бұрын
If that's the case I didn't get the point across well enough. Fair enough though.
@FISS0072 жыл бұрын
The main takeaway is: Use whatever works, is clean and saves you time =) Thanks for sharing.
@oscareriksson94142 жыл бұрын
Cool! Alternatives are good.
@jeffrdrama79842 жыл бұрын
How do I know if this guy knows what he's talking about?
@CodeOpinion2 жыл бұрын
You don't😆
@parlor31152 жыл бұрын
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.
@CodeOpinion2 жыл бұрын
That's pretty much what I leading up to when I was passing in the date to the create order.
@eltyo3402 жыл бұрын
@@CodeOpinion Any F# videos coming up? :p
@parlor31152 жыл бұрын
@@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.
@sandromagalli85872 жыл бұрын
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 Жыл бұрын
All of these examples are just testing things you've explicitly created in the test - they provide 0 value other than test coverage.
@georgehelyar2 жыл бұрын
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.