An Exercise in Refactoring - Live Coding in C#
I'm joined by Rosario Martone as we navigate some code that desperately needs to be refactored!
View Transcript
to yeah I guess we are live so that's awesome um checking I have chat on my side by the way so you won't be able to see chat um but I will try to as I'm coding I will try to monitor the chat and stuff as well um most so it'll be a little bit yeah it'll be a little interesting I'm probably gonna be coding stuff and someone will say like hey Nick that's totally wrong and it's G to be like oh no um but exactly we'll take it we'll take a deep breath and we'll get through it um so I wanted to say first of all thanks for joining me um you had reached out to me about going through a refactoring exercise and uh I thought that this would be a cool opportunity to just try and do it live because what better
way to refactor stuff than to see it kind of break and fall apart and try to put it back together so um yeah thank you for for reaching out um thank you I W to just double check okay it looks like people are able to connect I wanted to make sure that it's not just broken off this platform so we are on Riverside here I'm going to be streaming my desktop in a second um but for context uh what we're going to be going through today is a live refactoring exercise so fingers crossed that this goes pretty smooth it's going to be in C and Rosario you had mentioned uh so you you got my course on refactoring and there's still some things like you you kind of want to navigate and see if we can have some other patterns for stuff and kind of
just improve overall refactoring skills fair to say yeah I'm pretty sure there are uh tons of things I still need to uh navigate and uh improve on but I think um one of the best way as you say it's live streaming or you know just people with other developers because um you know we can share our knowledge and we can learn new things every day that's the main thing yeah absolutely so for folks that are uh are watching this live if you if you have questions and comments put them in the chat as we're going uh I'm guaranteed to mess things up as I'm going through this it's only reality so that's fine um and if you're watching the recorded version hopefully you find this useful and if you want to see more stuff like this obviously just comment send me a message let me
know and we can go through more stuff like this so uh again thank you for getting this set up uh I want to be able to kind of chat through before going into code like what refactoring is because I think there's a definition of refactoring that if you were to go look it up on the internet or find it in a textbook it's very clearcut um and it is essentially that you're going to be changing code without changing the behavior that's ultimately the goal when we say refactoring so if you have a class or you have a method you're like it's not organized in a way that I'm happy with for whatever reasons that could be many things uh it's not testable it's not readable it's whatever but the end state is that the behavior does not change generally that's the accepted way that people
will talk about refactoring but I like to mention that I I think in practice the definition of refactoring extends beyond that and people might say well then that's not the definition of refactoring and I get it but in practice uh when we're working in teams with other developers and stuff you will hear people say I need to refactor this and they generally mean something between a refactor and a local rewrite and sometimes when it's pretty nasty it's more than a local rewrite it's like a it is a big rewrite but we end up referring to these things as refactoring because the goal is still that you don't want to change the behavior but like sometimes the behavior is going to change along the journey so I like mentioning this because I see people will debate like you know you should be able to refactor that's
why your tests won't break and it's because if you're only testing the behavior there's no way your test should break but I just think in practice that very strict definition is very hard to apply in many of the code bases that I've ever seen in my life so uh how do you feel about that as a working definition of refactoring I think it's the best way to explain what refractory is and also I think even when we are writing our own code at some point we go back on what we did uh the day before or the week before and we just say oh actually I should rewrite or refactor this side because it's too messy or because you know I I was in a rush and I at some point you know you just get the job done but you don't pay too much
attention on what actually you are trying to achieve if it makes sense yeah yeah for sure so you know for the most part like I said I want to make sure that the goal that we're talking about is generally not changing behavior however I I like adding the caveat in that uh depending on how big the refactor is and in practice a lot of the time there is some amount of behavior change just depends on the scope um if you're working on a team where you can scope down refactoring and stuff like that to be very small and truly just uh not changing Behavior great I just have not really seen that work in practice and that's why when I talk about it I like making sure that people understand even if you go outside the you know the lines of what refactoring is and
it starts to become a bit more of a rewrite like a lot of the same tools that we have apply in the same way so that's my it's my little Preamble um I'm gonna go ahead and share my screen so give me one sec here I'll get this Visual Studio window pulled up and I want to double check on the stream that it looks okay because it's going to have our faces and visual studio so okay I think it should be legible it looks like our faces are at the bottom in Riverside they show it to the side but on the stream it's at the bottom so it's kind of funny um I like to just double check that stuff to make sure that nothing's going to get cut off or anything weird like that but okay so uh Rosario when you joined the call
I said I put together a quick little program it's very basic we're going to hack it up and try to improve it but um the very basics of this program are going to be that there's some amount of business logic and we can talk about um you know extending this out because there's going to be some different scenarios oh Dan is on LinkedIn saying it looks good to him in terms of layout thanks Dan I appreciate that so this will be some business logic we could talk about scenarios where there's other people consuming some of our code so for example you have a package that you've published or even within your own organization how do we make sure we're not breaking other people when we're making refactoring changes so this is just one spot that's going to be consuming this API client that we're going
to be talking about so this is the API client that we're probably going to spend the most time on doing some refactoring um this particular setup is generally not something I would totally recommend but this is why it's a good opportunity to refactor it so there's a few things set up in here that I'm not a big fan of and uh I kind of I'll walk through them and then Rosario maybe I'll kind of get you to to see if there's like things you want to prioritize in terms of refactoring um but I think there's a lot that we can touch on so if you feel stuck I think we can keep picking things apart but super high level all that this client does right now is it has a method on it to log in there is no real endpoint so this is obviously
not maybe it is a real website but it's not mine um so we're going to make a post request to this URL and then we're going to have a username and password and we have the username and password is user and pass just hardcoded into here and basically if we get a successful response will return true that's all that this thing does right now so if you were reading this how would like what are the things that you might want to go change and adjust uh and maybe we can go steer from there yeah first of all I can see um there are some new uh which I think we should move out basically uh this is because we we just want to first of all we want to improve the testability of the code uh so we want to make sure we are able
to test every single uh part of the meter or the class itself so the the first thing I will do in this case is just trying to uh inject in some way uh in this case the HTTP client uh that would be the very first one and then you as you said uh we should parametrize the the different part of the the post itself so it will be the URL it will be all the information maybe not um passing as different uh separate strings maybe we could opt for an object so in the future if there is something else all we have to do is adding the attribute to the class rather than changing again the signature of the meod um yeah that those are the main things I guess cool no I that that works for me so um yeah it's and maybe before
I start refactoring some of this uh because Rosario I know you you had mentioned this to me and I don't think we chatted about it um ahead of time this call but you had mentioned it to me in our messaging so we want to make sure that we are writing tests on this stuff but right now there are no test and one of the reasons why there's no tests on this and I want to touch on this for just a moment is the way that code is written would already be very difficult to test and you might say well how is that hard to test Nick you just call login async and then I would say well yeah but we have to go make sure that this server is running we have to make sure like you have now A system that is truly external
to you that you're interacting with and depending on what type of test you're running and your the context you're working in that might not be a very good idea at all in fact in this particular particular case it would be quite bad because this is just going to throw exceptions like I have no control over that API uh it's not going to be a good time so right now there are no tests over this but when we talk about refactoring one of the things that you would probably want to do is get tests in place so this code is not very unit testable currently so some of the first suggestions you had about trying to get rid of newing things up can really help with making things testable so I think that's a great spot to start but um the challenge of not having tests
in place ahead of time means that we are I I just want to acknowledge it like we are at risk of breaking the behavior because we don't have a regression Suite to go against so so I'm not going to code that regression Suite ahead of time here uh again because how this is set up it's just not going to be realistic but if you are looking at this code and trying to compare it to something that you might have in your own code base at work whatever it happens to be you might have maybe not something so exaggerated like this where it's like an HTTP client maybe it's um you're writing to the file system and stuff and you're like okay like I can at least maybe encapsulate some of that so I can do around my test like a a cleanup and you feel
okay about that so and then there's some comments coming in on LinkedIn uh model passing instead of hardcoded string so yep that'll be the second refactor we go to so that's a great call out and then someone else just said use HTTP client Factory here so that's exactly the first refactor that we're going to look at is pulling this away and using the HTTP client Factory before I do that someone just asked uh do you mind telling me more about regression test so yeah just to kind of touch on this a little bit further the the goal when we're refactoring is that we want to make sure we're not breaking anything right that's I mean you could main thing exactly you don't want to you want to say yourself some time so right now we have this class but there are no tests at all
over it there's no unit tests that are testing with mocks and maybe touching some implementation details there are no functional or any other you give it a name we don't have a test for it there just aren't any so there are no tests in code that would go exercise this method and then assert that some expected behavior is there so generally we would like to do that and I would highly recommend that any code that you're going to refactor you try to make sure number one do you have test coverage in place do you have the right test coverage so I mentioned if you have mocks and stuff in your unit testing again some people are going to be triggered by this but sometimes you will touch and change the implementation details when you're going through this so tests like that are not very great
for refactoring the method that you're working on they can be great for telling you you're not affecting other things um but you want to probably have something that treats this like a black box and you're doing some functional or integration tests with this so and then someone's uh mentioned again on LinkedIn adding interfaces and dependency injection yeah so you you're going to see very likely that that's the direction that we're headed in as we start to refactor this but we're the goal with this is we're going to do it step by step and then you can see some of the different uh questions we need to be asking because if we jump right to things like interfaces and depend injection which by the way that would be something I would want to go do especially if I owned all of this code I would say
hell yeah like let's go right to that um but there are some considerations and I think that's where we're going to see some interesting learning here so I'm going to go ahead and get rid of that HTTP client I'm going to I'm not going to use a um primary Constructor because I think some people will be upset by it so uh public our API client I'm going to pass in an HTTP client Factory I don't want to do that co-pilot it's good most of the time but sometimes it's not quite what I want and now that we have this passed in what I'm going to do is make the client here and this is essentially the very first step in refactoring so uh I'm checking there's Rob mccrady who's uh who's great on LinkedIn we have some good chats mentioned abstract API client base accepts
iht HTTP client Factory and holds that in a protected property Define the I login API with login methods yeah so you can see he's kind of going in the direction I think we're going to try to head um inject a mock of your login proxy yeah so he's basically describing sorry I'm kind of just reading that super fast Rosario I know you can't see the chat so I apologize but he's talking about pulling these pieces apart into smaller pieces pieces and we can start to compose them so that's an option I think we are going to head in a Direction that's very similar to that but even though this looks like a very trivial refactor that we've just done right in theory this should go make an HTTP client for us the same behavior that we would have wanted this may not work and this
in fact might not be a refactor that you could ever go land just like this and Rosario when you reached out to me one of the things you men was like Constructor parameters and what was the challenge that you were trying to navigate with Constructor parameters in doing something as simple as this yeah sometimes for example you could have already a constructure in place uh so for example in this case we just added a new one because there wasn't so that's an easy one but let's put an example when you have a class you are going to um um update and then uh oh that is already the constructure so how I'm going to add uh any parameter or how I'm going to inject anything in structure without destroying or creating any regression for the people already there basically um so that was a an
advanced let's say part of the the refactoring itself so how what I'm going to do in this case for example if the Constructor is already there and you have there is already the high loger inside the standard meter and then okay in this case we are adding the HTTP client Factory but that means that we are changing the the existing constructure so what are the options here should I have a a overload of the constructure should I add for example um optional parameter to the constructure which are the pro and Cones on each one of them basically right and so great call out right so yes we went ahead and we made sure that we didn't change the behavior of this class I don't have tests that are proving that but we will get to that but now and so Rosario even in this example
though we actually broke the calling code right so yeah like technically we added a Constructor but the default Constructor that's there just has no parameters so we literally broke this code but in this case I own this code I don't care I'll go update the calling spot right I I'll go pass in an HTTP client Factory not a big deal but let's assume that this was a uh class that was in a common project that other people are using or it's in a nougat package so oops did I okay yeah so this is I'm going to make this one Rosario's business logic so if I owned this one in my code and Rosario you had your own business logic somewhere that had to go do some stuff and had to go use this client I couldn't go publish this and ship it because if you
updated you would break yeah right if if we have new get packages with versioning this might be an acceptable thing where we're like look there is no other way around this we must break this API and then we would have to you know maybe we do like a a major revision or something we have release notes we're like sorry everyone if you're going to update expect this to happen but this I mean it can be a conscious decision you make and in many cases there are going to be situations where you're like it's going to be too nasty for us to try and make this work but we do try as much as possible but again a simple refactor like this depending on your context you might say this is actually creating a ton of problems for other people in all of the spots that
we own in this example it's just one uh very easy to go fix we pass in the factory but you know this is something we have to keep in mind so um I think it's important that you do talk about these types of things um so just checking chat here again question what net version you're using this is net 8 uh I don't know if that's really going to matter for a lot of the stuff we're looking at but that is what we're looking at here um someone said I'm sure you want to suggest primary Constructors they they tag someone but I found them limiting in some cases so this is an example so I'll just tou on primary Constructors for a second the thing that I don't like about primary Constructors right now is that we don't get the read only uh keyword that
I'm highlighting I think that otherwise because I use dependency injection for everything it basically saves me like those three lines where you see HTTP client Factory get reduced into one line and in the code I write personally I am never touching things that are pass into the Constructor so it it works for me in my own code because it would be a huge code smell if I saw myself reassigning something to http client Factory and it wasn't read only but again if you're working in code bases with other people you have a team you want to talk about what standards make sense for your team because if everyone agreed with me great primary Constructors awesome yeah but if you have people that that don't align with that it's going to be a bit of a a headache and you just want to make sure that
you're setting people up for Success so okay we have this situation now where we said Hey look this might go break other people so one option we could consider is that we make this something like an optional parameter so we could go do this and that would mean that we have to go do this to make it nullable just to make the compiler warnings go away now we have one more compiler warning because technically this can break in some cases right if I scroll back up we can see that this code up here is happy now so I'm not going to break I's usage of this and I'm not going to break my own usage of it so that's good news but down here this is still not right so I think when I tried this earlier co-pilot did this so this is something you
could look at and I think I need this here so you could have an option like this where you're like hey look we had this code released before we don't like newing up HTTP clients the way that this was doing it because this can lead to Port exhaustion and a lot of other fun things some resources being held around so instead of doing that we want to use the HTTP client Factory but people were already using the HTTP client maybe in their cases it wasn't so bad and if they truly want the fix they're going to have to update their calling of this but at least they have that option now so this could be viable it to me it feels a little gross um but again if you're trying not to break other people this might be totally acceptable yeah there isn't going to
be a right or a wrong answer that's Universal in all of these situations this is one tool that we have to use so Rosario any thoughts on this part so far anything else that you'd want to see change before we move on yeah this is what I will do in this case um as you said it's not breaking any existent um user and we are also covering the the testing uh now is a bit more testable the code uh for example Will D the interface to the class itself so that would help um in um mocking the the tests as well yeah and I think the next part that when we were talking about this offline right one thing that is a bit of a headache still is that the client here it's not this oops right there is no interface for HTTP client unless
someone knows something I don't which if you do please tell me uh but we don't have this so that it's we will make one uh so this is more testable than it was but it still leaves a bit of a problem because if we want to go test this and we're going to use mock to make sure that we're not truly going to go hit the internet because let's say we want to write unit test not like an integration or functional test over this we get this as an interface but the resulting HTTP client that will do the communication we can't simulate that it's still going to go out to the internet um we got a question in the chat is it good practice to create optional Constructor dependencies I think that anything pass to the Constructor should be essential for the operation of the
object null to me feels like the dependency is just a method param and not Constructor parameter any advice yeah so this is It's a good question this is why personally I'm not a huge fan of doing this I almost never use uh optional parameters um for a couple different reasons one is like I actually like having I would rather have more explicit Constructors if it was something that was variable I find optional parameters they force the ordering because you have to put them at the end sometimes it's not aligned with how I like to think about things but not not the end of the world um to the question though it seems like more of a method parameter so you could you could go instead of passing in this in the Constructor I'm going to comment this out for just a moment what we could
do is we could go pass it in here but this is going to change the signature that we're working with but you could do this and you could actually do something like this where because we didn't we didn't explore this one ahead of time but maybe you leave this old one in place right where it's just doing this and then we actually make a new method that's it's not going to take one like this right you could go do something like this and then we have a different refactoring thing that we could go look at right because now we have this duplicated code we could go pull this out so this is another viable option but the question I ask is like does the HTTP client factory make sense to be passed in on every method call or is that something that you truly do
want to be part of the Constructor my opinion on this is that this dependency is something I would want as part of the Constructor however using it this way does make the refactoring a little bit easier because now we don't have to worry about all of the you know breaking Constructors and stuff like that it's just if you want the new functionality call the new method so that is kind of nice another I'm not sure if I'm am right but I will say maybe the second the second method you could make it private and then from the existing method you can just make a call to the new one and then in the new one you can set up whatever it is needed so in that case you are not exposing the uh optional sorry the overloading um so they will still get the the
method as the it was but in the background you are doing something complet different or something better in general yep that's exactly right so you could do something like this where we pass in so I'm going to do uh AIT log and then we would have to go basically I don't even know if I can new up an HTTP client Factory there isn't a I think I'd have to go make a wrapper I don't know if we can access the the built-in one but the point here is that you would make a new right you'd have sorry autocomplete over there you could do this kind of thing and basically wrap that behind the the scenes so you could do that for sure um there's a comment the chat also good practice to check is cancellation requested we don't have that option really like we could
do that here um you can do something like this right at the start of your method if you want um that needs to be a semicolon not a full colon so you could do that um but basically you're this is going to throw and cancel anyway so you can save one line by doing it right at the start if you want that's totally viable and then I see Rob mccre so Rob Rob's uh comment here is is something I'm in agreement with it says injecting dependencies at the method level smells a little like impending SRP violation and I agree like I like I think that it can work and like truly this this functionally will operate it just kind of goes against some of like the design principles that I want here so he says the only reason to have a particular dependency be nullable
and valid in that state is if you got some methods on your class that don't use it feels like your class is responsible for too many things so um yeah I I agree with that so I'm going to go back I'm G to go back to some of this code that we had we're going to stick to the Constructor uh I you know I'm calling out that I'm not a huge fan of the the nullable part oh I deleted too much oh no that's okay I'll put it back super quick so come on co-pilot thank you don't have to do any work now so here's where we've gotten to so far we've called out that it's a little bit more testable which is good we still have this problem that if we want to go write tests this thing is still an HTTP client which
is going to go connect to the Internet so not yeah now something that I'm not familiar with because I have uh dug super deep into HTTP client someone might be able to if you're watching this live or you want to comment on it later I think there might be a way that uh when you create an HTTP client so let's say that we are using a mock for the factory and we pass in our own HTTP client there might be a way that you can configure the options of the HTTP client itself to truly not go out to the internet like you might be able to put in some request handling stuff on the HTTP client itself and that way we don't need to go create a wrapper around it with an interface to be able to mock it I think that kind of functionality
might exist with HTTP client but I'm not totally familiar so I just wanted to leave that as a a thought exercise but we can move on and keep kind of seeing what else we can do here but you had mentioned this part was kind of interesting right so parameterizing some of this stuff um so this is something that if we keep going through this exercise of like how do we not break stuff we start to get more and more uh problematic as we chip away at this right because if we keep doing the same pattern we'd say okay well we need to have username and password have a default value or else everyone has to know to pass them in and this is probably something where I'm like I think we have maybe a breaking API change but how would that look right it basically
starts to take shape like this and you'd have these be set to their default values which by the way anyone who's reading this code if you're going seems kind of weird that you'd be hardcoding this stuff in the first place yeah like you don't want your password hardcoded so this it's a contrived example I apologize practice I agree so we don't we don't want to do this in the first place but imagine it's doing something else and you had hardcoded values that's not your password so if we have this kind of thing going on you'd be able to do this so we have these fields passed in and then we can start to replace them here one sec uh this is going to be kind of nasty because the Json that's okay uh so we need to go put user here yeah it's not cut
off the screen yet so we're still good to go um did I miss I put an extra one in there we go so now we've parameterized it and we didn't break anything does this feel good though like it's I mean it's kind of nasty but truly we've refactored it to be parameterized at least um if we did this in multiple steps though so let's say that we released the other code would we have broken anything if you wanted to pass in an HTTP client Factory I don't think so um sorry what I'm trying to say there is like if we had done this say the last version is something we published right and it didn't have these things on it so we published this and now we're adding these in are we going to have a breaking API change this is where your test would
be really helpful to to see like did my test literally break like not in terms of it's not uh it's it's running and it's failing but like the test doesn't even compile you have code calling a code path that is now broken like that's a good indicator that you have a a breaking API change so um that's another main point why you should have in principle the tests because as soon as you uh refractor something you can easily run the test and see step by step if you are introducing something wrong basically totally so let's maybe before I do this change let's go jump over to some test code I know this code is now not going to compile not the point and in fact I don't even want to run the test quite yet I just want to set it up to call or
to make it look like it's calling the API so that's my caveat here is like we're not truly testing this thing yet but um if we had our API client we made a new one so this is the the original way that we would have done it and basically through all of our efforts of just adding in this we now have proof that there's a test and you do right you have some test that you're running but like we know that this code is going to compile because we can see it then we said oh like we need to do with the HTTP client Factory so now we have to go pass that in like I said I don't even know if I can do that um let me go I don't know how we access the other one but let's just say I don't
like using fakes and stuff in the first place this would generally be a mock um I'm just doing this super quick so cuz I forgot to go add mock as it a as a n get package before starting this well get to that though I think so we have this code now so now we have these two code paths that at least compile right these aren't full tests but they at least compile now we would want to go do the next part which is by the way the names for these are going to be terrible so I apologize um so we have this scenario where we want to be able to add username and password right so we have these two things passed in obviously this doesn't exist yet because I just commented it out but if we go put it back in interesting right
it does work oh doesn't appear it doesn't work so this is a situation where we've just proven that if we go add these two things in like this we actually went and broke the consum assuming code so again for folks watching this like the goal when we're refactoring is to basically have the same behavior here that's the goal but you can't just ship this code if you're not thinking about the implications of where it's called like that is a huge huge thing to consider and it's often I see people start refactoring code I mentioned this at the beginning of the stream but it starts as a refactor and then it creeps into a local rewrite and then into a big rewrite and then you keep having problems Landing stuff because you have to keep going to make changes to all of the calling spots and
soon you've touched every file and you go finally my refactor is done and it's a whole new product so this is uh something you need to think about to scope down how far you can go so how do we solve this I think there is another solution here it starts to get starts to get gross in my opinion though so we can do this kind of thing and we can Chain by the way I recommend chaining Constructors together if you have to do this kind of thing the reason I recommend doing this is that um this type of thing is easy to mess up sorry let me uh this one's not going to work it's easy to stop to start missing assignments of things if you have to copy paste them because as this type of stuff continues to get copy pasted inevitably you miss
setting up something and even when you're chaining Constructors together it gets pretty nasty pretty quick because you might be chaining to another Constructor that works but you're not passing in a piece of data that you have so it it can get pretty gross but this is how I would at least recommend doing this one um and I think on this we could probably do can we do that still doesn't work I wanted to see basically if we could just use a named uh parameter and then not have to pass in the defaults again because that gets kind of nasty because like I don't want to do this because you can see the defaults are in two spots um can we use a constant if it's constant it might work yeah so we could do this now we don't have it duplicated necessarily come on co-pilot
thank you so much that's one of the nice things for live streaming is uh if co-pilot's doing its job I just have to kind of think about things and it works um but did this work because this one's still not working up here so what did I mess up it's ambiguous now no it's right so this is like this is good evidence that this stuff is not as trivial as it seems on the surface right um just checking in the the comments here I suspect it's imp that implicit Constructor calling your test method yeah saves keystrokes today but gives migraines tomorrow here's the migraine right now uh is fine to inject primitive types as Constructor arguments don't they make di feel awkward yeah so you're predicting the future of this live stream we're going to get to hopefully see some dependency injection and why having
Primitives like this is not great so in this current code if we look at the calling locations there is no dependency injection and this felt difficult for me to write because I haven't written non-dependency injection code in like uh literally the last time I did it was for my refactoring course and even that was like how do I type the code to not use dependency injection um I feeling like the password and name should be method prams instead so yes but I want to give you a situation where that may not be the case uh so I think that that is a good point um and sorry Rosario sometimes I'm reading the comments out and I'm not saying that I'm reading a comment I just realized that so uh if it seems like I'm having a conversation with no one it's because there's a comment
uh okay but yes I think that uh it makes total sense to be able to say that we want to have your username and password Here now just to highlight a situation where that's not necessarily the case is if you need to configure stuff with dependency injection once you're never actually logging into other accounts and you want it to be passed in through a config you can totally just pass it in through the Constructor that has the config and no one else that's calling has to know about the username and password you basically do it via configuration so someone just says I need to log in to be off I don't know what the username and password is but the very fact that I have a reference to this thing means that I can be offed that's a viable option I'm not saying it's right
or wrong because I would agree that generally if you're writing something that logs in usern name and password are pretty good parameters for that so uh your context and your mileage will vary here we still have this issue of these Constructors colliding with each other so what can we do to fix that Jesse bud what is going on here we're refactoring some stuff and we're trying to see if we can not break code as we're going along so that's the goal so simple one yeah what you could do in this case I think is now we have more Constructors chain together right we've gone from having one Constructor to one Constructor with an optional parameter to now three Constructors it's pretty gross um and in fact what you could do on top of here is start removing these right so we could do that and
actually this one like I said for chaining stuff together I actually want this one to go right to here and it's not it's bypassing it entirely so you can start to do this type of thing actually we can't sorry I'm jumping around a lot but I'm trying to see if we can clean this up for not having defaults and stuff passed around but you can see that at this point up here we need to be able to have an HTTP client Factory there isn't another way that that's going to work so I'm going to go undo some of this uh can I will this one work still won't no so let me leave this I don't like having two different ones so these both call into this one down here I don't really like that the comment in the in the chat was simple solution
is to start again yes um that makes delete it all start again you say it's gross but the hu huge amount of net library has Constructor overloads as default design Choice yeah uh they do I'm not saying I agree with it in terms of how that's done but it's a it's it's a pattern for sure I just I'm not a huge fan now if you're starting to use things like dependency injections sometimes the ergonomics of calling these Constructors can change you have to think about what is the caller going to think when they're typing new my uh you know our API client a lot of the time if we're using dependency injection you don't even think about that you're just like list my dependencies someone's going to pass them in we're good to go so there's a lot of things to consider and that's why
Rosario when we were talking this it's like the more tools and approaches you have at your disposal the more options you have when you're in different situations because you're going to find you know different things have different answers for you and then he goes I'm on your site overload should be banned at all costs yeah um so unfortunately we're approaching this with overloads so that we can stop other people from being broken and I want to reiterate that because again we could we could jump to interface as in dependency injection is one big step we could do that but depending on your code base and who's consuming your your interfaces or your classes you just might not have that luxury so at this point if we go back to where the tests are you know these are not actual tests for anyone that's just joined
these are just proving that we can call the constructors that we're interested in so far this works works but we were talking about this part being pretty nasty with two strings passed in here right um and I should have I already broke this is why you need test I already broke this functionality when I was messing around I removed these so that's what we should have had so there we go okay we talked about this though where you're having a username and password right let's pretend let's pretend for a moment the first time we made this code change I know this is going to make some people cringe it's hard for me to write this but let's assume that someone writing this API originally decided I want to have password than username now a refactoring that you can do this is a common thing is
to reorder your parameters right someone might have said hey look I don't know why you did this in the first place I'm sure there was a good reason I can't think of a good one but I'm sure there was a good one I think that we should refactor this to be more usable for the callers it should be username than password because that's what people expect I think that's a fair thing to say people want username then password so again this code here is going to be wrong right we would want to go this is what it would have looked like your p your username okay so if we wanted to go change the order of these things we run into a very big problem when we think about the code that already exists that we do not own and even for the code that
we own we have to know all of the spots to go touch so I think in Visual Studio we might be able to change the signature you should be able to just do oh I just realized it's sharing the window but it's not showing the the the window for the popup so you guys can't see that I'm going through a refactoring menu in visual studio so that's not very helpful um let me back up and explain what I'm doing so I'm going to watch the stream and make sure that if there's no visual popping up I can explain what's there so you can't see my context menu which sucks so I'm right clicking on our API client at the very top of the menu quick actions and refactoring there's an option there that says change signature so I'm clicking on that and it puts up
a dialogue box that has all of your parameters in it so you could use this dialogue and switch your username and password around which I've done by the way this was already wrong forget that um but it basically just went through and changed the order for us and if I go look at the calling spots it also changed the calling spots this is very powerful if you have all of the code that consumes your API in the same solution yeah because Rosario if this code that we had over here that says Rosario's business logic if this was in your own Solution on your machine doing some stuff that's unrelated to me I just went and did this refactor yeah and I'm like hey look it works it's perfect it compiles on my machine now your stuff will be completely broken if you update to this
so this would be a breaking API change okay so what can we do to try and help make that better well one option is to try and essentially pull these into something like a record that we can pass along we could use strongly typed IDs we could do a combination of the two thing two things sorry um password would not be a strongly typed ID but you could make username a strongly typed ID and that way it will it's not going to fix that things will break but it will make it very obvious it will be a compilation error instead of the username and password being flipped and then you have a runtime error so that could be something we look at so Rob mccrady saying resharper is great when all your consumers of your methods and classes are in the same solution yep if
you're extending the public AP on a library package you have to be sensitive to the installed user base out in the wild absolutely and this and admittedly I think in my course I tried to tou on this but maybe honestly maybe not enough attention to it because I think so often we're thinking about refactoring don't change the behavior very important but you need to think about all of the spots that are calling your code and especially for people that are working in smaller code Paces or your own projects it's very easy to be just hyperfocused on my own usability so at this point if we wanted to go pull this stuff into a record it depends did we already ship this code right are we going to have yet another thing that we want to go extend onto here I might take this opportunity to
say let's say that we didn't ship this because I think the exercise is going to become more and more challenging to move forward if we have to keep coming up with API so let's say that we were talking about shipping this but we didn't go ahead and do it we said you know that's not good enough we should try to package these things up so what you might do it's something like a record type login info I'm terrible with names but um there we go co-pilot knows what I want we could go ahead and do something like a login info and we could pass that in I scroll too far so we have that and username and password do we want to do that in here maybe again to clean this up I would do there's no I don't think there's a right or wrong
thing here maybe I want to store the whole login info inside the class that way if I add more stuff later I don't have to go you know trim out the other fields and stuff again not a right or wrong thing just a a choice for right now that way he here we put this in this is starting to go off the screen a little bit so let me just go ahead and uh clean this up a tiny bit some people will say that's not making it clean I'm sorry I'm just trying to do this to make it more readable there we go that's Lovely isn't it okay um so now we have this passed in but this is all busted and weird now so we need to go make sure that we're assigning this login info so this one pretty simple I think we're
okay here this one and this one are going to be a little bit odd so if we need to go past so go from this Constructor that only takes the client Factory we need to be able to call into this one so we should be able to do something like I kept the constants that's good you could do this someone might say well now you're going to be allocating a new login info every time so we can go one step further yeah we'll make a default login why doesn't it like that oh need a type yeah there we go uh I'll get rid of that pull this down just to kind of make it a little bit more readable I'm trying to be conscious of how far on the screen it's going on the stream so now we have this default login so we can
put that here and that means we can go here my goodness sometimes I forget when you have a microphone in front of your face and you're trying to type stuff like you have it in your peripheral it's a little weird I'm just checking the chat to make sure there's probably some comments coming in here resharper is great okay we read that one uh minor s version update when you're adding optional new features you might need to add some overloads yep getting rid of those overloads can happen on the next major package version release yeah and you can use that as an opportunity to be able to signal to Consumers like hey we did this thing you got this much time to figure out how to how to stop using it if you want to continue to update so Rob also went on to mention sometimes
we have to incur technical debt absolutely but these cases are perfect examples we're taking it on as an investment where we have concrete plan to pay that Tech that back with the future version right and I think that's totally fair you might even plan for that and say that's what we're going to do and then it turns out that like you never do it and you're able to work in the code base still and it's not really a problem but at least you planned for it and had this thought process of here's the direction we'll go if your priorities and stuff change later and people are still able to work in the code base and it's not a nightmare great like you basically just avoided doing work and that might be totally viable uh what else is going on here more That's a classic Microsoft
bait and switch okay yeah let's let's settle down with the Microsoft jokes I gotta I got to go to work soon remember that at Microsoft cool okay so we have uh we have this API kind of set up for us again if we go back to our code here sorry we said that we weren't shipping this part right we said this new one we would have something like this and we can prove now that we have a scenario where this will exist we would write tests on it but we have coverage over calling this code to at least make sure that it compile time it's not going to blow up for us so pretty good option to parameterize stuff again just for if people are joining the stream and stuff yeah login info probably a good fit as method parameters here I mentioned earlier there
are options where you may not want to do that because you don't need all of your callers to know the credentials or have access to them you're just giving people who have access to this thing Authority okay so now Rosario what are your thoughts do you have other things that you would like to see on this are we ready for some tests or anything else you want to see us kind of pull apart on this before we move on uh I think we Sav for the URL of the API right we were thinking to parameterize as well yeah we can absolutely do that so and I was also thinking maybe we could D the interface to the class itself yeah so the HTTP client part yeah having that one wrapped as a proy yeah let's let's do the um the URL one yeah Okay cool
so let's do the URL one let's take a different approach because we've been jamming stuff through the Constructor um again we could have done that with the login info I just there's no right or wrong thing here it's just an example to walk through stuff but let's pretend that this one we're like this one has to be configurable on on the on the method name or on the sorry the method signature itself is this a good choice like probably not I probably should have done it the other way but it doesn't doesn't matter it's just a working exercise so if we wanted to the goal here is we're saying uh basically whatever API you're trying to hit that URL needs to come into play here and just to so I don't lose that like that's what we're saying but if we go back up it's
going to break code we can't just we own all of the calling sites and that's a a sufficient amount of work for us that we're happy to go change but in the example we've been going through so far I can't make that assumption how do we live with this we could use another optional parameter like this but one of the reasons I don't like optional parameters is now I have to do this oops I need an n on token so you don't have the luxury as soon as you put an optional parameter in anything else after has to be optional as well um some people might say well cancellation token I don't care like Let It Be optional because most people on their async apis do I personally like enforcing it in any API that I write that's asynchronous I force it I don't care
if it's more ergonomic to not have it like I want you to be thinking about passing in your cancellation token um so the point here though is not cancellation token specific it's if you're trying to do this with your API and you want it to be default you're basically stuck putting it on the end or making everything else before it have a default value which can be pretty nasty uh I'm not a huge fan of doing a default here this is where I might take a different approach again it's not right or wrong these are all things that you have to weigh out for how you want your code to exist uh let me go ahead and put this back for a moment what I would probably do from here is just have another overload like I would want to hide some of this we
kind of talked about this earlier for having an overload and then in the body of the method we're doing a little bit of extra stuff to kind of hide that from the collar so I might go ahead and do something like this where I force this to happen and then up here this one does not have it and I would get rid of that I'm sorry I'm jumping around a lot here but we get rid of all this stuff in here and we have this we could pull this into a constant as well and then we would go call this method down here so this is something where we can get more control over the signature by not having defaults and having to change the ordering or that kind of weird stuff so again just an option uh no C pilot not quite what I
want that's what I want thank you it'll get it at some point so we do get this kind of option uh I just realized there was a question mark on that guy there should not be I saw a compiler warning we got to watch for those but now we have if we scroll back up these ones are not broken if I jump into the calling code we can see that it's doing what it used to do right we're using that default uh I would maybe let's go is there a good refactoring option for this uh you can't see it because you can't see the context menu but I right clicked on it one of the refactoring suggestions was introduce constant uh and then I can do a local or a constant at the class level so I'm going to go do that pull it up
here there we go yeah so a nice constant pulled up uh this is you know that this is kind of an opinion some people may want to say hey I want my constants to be local scoped it's only used in the one spot to find it in the spot um these things are probably configurable anyway reality is like these were probably not good things to hard code in the first place but you may have code where you start it off that way and you want to keep building on top of it I think that's another important note to make is there's so much stuff that you will see in posts on LinkedIn in blog articles and YouTube videos and it's basically here is perfect code and here is how we work with perfect code to do more perfect things and the problem is that as
soon as you start working you will never see perfect code ever yeah so you you have to be prepared to navigate these things and so Rosario would you say that's fair from your experience though that like you know I can I can guarantee sometimes I I had look at some really bad code much worse than the one I wrote so you I perfectly I agree with you right and it's funny because you know when we say the codes bad like I've I've written code sorry I read code and been like man this is bad and then I realized I wrote it um but the point is like it's not that when we say it's bad code like we always believe or we should believe that whoever wrote that code at some point in time was doing it with all of the information they had at
hand doing their best job with what they knew right it's not like someone purposly was like I'm going to write this really crappy code and upset someone two months from now or two years from now no one hopefully no one does that but unfortunately as code bases evolve and patterns evolve and people start doing you know better practices there is going to be like these landmines we come across where we're like oh crap like now we got to deal with this so it's just the nature of a reality I'm going to check the the comments in the chat before moving along here um in defense of microsoft.net and C is one of the best languages ever created ever and it isn't their fault we including me make poor design choices yeah um that's a it's a really interesting comment I mean it's you can't control
how everyone uses stuff I think there's probably some things that you know when designing the language they can keep What's the phrase like the pit of success like you can keep pushing people into the pit of success but it's really difficult with such a generalized language you can do so many things with C it's really hard to force people into this pit of success when you're saying here's a hundred ways you could do it and we're giving you flexibility so that's an interesting one um the CLR is artwork absolutely uh Rob is saying to Jesse I blame the product owner and scrum Master for my bad design decisions yep you're rushing me yeah um cool wait if you're not yeah so Jesse was saying about the cancellation token if you're not consuming the cancellation token you have a big issue yeah that's a great Point
um my toxic trade is not working harder to form the cancellation token habit yeah absolutely so like I was saying the way that I got around that because I had a about two years ago I guess it's been just over a year now when I when I canel it I had a side project going on as a a Fitness and Nutrition platform that I was building and one of the things that I realized was that I had written all of my my web apis and nothing was passing in a cancellation token and I said okay like I'm and I own the code I spend a lot of time on it so broke some of my refactoring practices I basically started saying in my bottommost layers in my whole application cancellation token required start dropping that in and then it's just compilation error like you know
300 compilation errors and Chip Away um like force myself you need to pass in the cancellation token so um it's a really good practice anyway yeah yeah it's uh one of those things it's easy to kind of say like I'm not canceling it just like go away like let me keep coding it's an extra parameter I have to think about but yeah we need them so please use them okay so we have the API URL passed in as a parameter now what we probably should do is go back to here again I'm not going to test these but we could do a log in async uh oh I made it void sure whatever this is not what you want to do you want this to be async I'm just trying to get the API calls in here right so we'd have this type of thing
and then maybe on this one we said we want to do it the other way also not a URL but that's okay right so we have coverage over these and if we ever want to go change we can see hey look the API is actually broken so again the point of your test is to exercise the code which I'm literally not doing here I'm not asserting anything these are terrible terrible tests I'm just trying to demonstrate that we have some call sites where we have the API we want to call so very minimum type of thing here okay Rosario the other thing you mentioned was this part here with the HTTP client um yeah and Jesse and Rob are talking about cancellation tokens here Rob says I confess to a poor understanding of the cancellation token concept I know it's important but I'm not 100%
sure why any web resources I could look at um I don't know if there's a good like off the top of my head a good resource um Jesse kind of chimed in to say it's not am I can it's am I being canell and I think that's the frame of mind you want to have you're in a method and you if you're asynchronous what you want to be thinking about is like someone on the outside May abruptly need to stop what I'm doing because they're trying to get resources back you're trying to shut down whatever it is I need to stop and you're off running off into outer space doing whatever calculation or IO operation you might happen to be doing the whole point is that when we have asyn code with the ability to cancel is that we can take something that might be
long running long running here is subjective I'm talking anything more than like you know microc seconds kind of thing if you're taking time to do anything someone on the outside should be able to say stop everything you're doing right now and where you put your cancellation token checks will will change that so I think that's probably how I would explain that but looks like Jesse's doing a good job in the chat trying to elaborate on that so thank you Jesse I appreciate that okay we got this HTTP client let's go make this thing better um like I said earlier I think there are ways that we could avoid everything we're about to go do here with this proxy class and interface I think that you can configure this thing to not go to the internet but the pattern that I'm going to show here is
about making things more testable with proxy classes in an interface and if you're like hey like yeah you can avoid this with the HTTP client don't worry about it cool um what about for using static file classes in the file system there's so many situations where when you new up something what you're basically acknowledging is that if you want to go write a test over this you must be using the implementation that's provided right here some people might say good I want to test the real thing and I say that's great until you don't have control over it for a scenario you want to exercise for it's no longer a unit test right that will be a kind of integration test at that point 100% And what I think's important to acknowledge about that statement is that someone might say yeah but I want to
write those types of tests and I think that's a fair statement but going back to Rosario what we were talking about earlier was this idea that if you have options if you have options you can make decisions about which option you want to use so instead of saying I just want integration tests anyway like screw the interfaces forget the mocks I would just say what if you got in the habit of doing the other thing so it didn't feel like more work to you and then if you want the luxury of a unit test or an integration test you can literally write both you can do anything you want because you've structured the code in that way code like this does not lend itself well to being unit tested because you're using real things that you can't control I put out some videos about this
the other day talking about almost this exact thing I don't control the HTTP client and if you wanted to write tests that prove that you have retry logic in place you can't you can't because you don't control what the HTTP client is giving back now again I think this class actually has some things you can hook into it's beside the point but when you don't control the dependency you can't simulate different behaviors so that's just could be also a database connection or could be as you were saying I'm going to write on a file so what happens if I I can't so in some way when I'm writing unit test I need to be 100% sure that I can test almost everything yeah and like and do you need to test everything probably not but having the again it goes back to having options if
you're if you were in a situation where you said it would be critical for me to know if this Behavior exists you would need to be able like so I'm writing a file and I need to be able to retry as an example okay how would you go about simulating I need to have a file that is open with some other process like something's holding the handle I need to try writing to it once I've tried and it fails make sure that I catch my exception now release the handle now try to write again like if you're not mocking things out your ability to test becomes completely like overwhelmingly challenging depending on your situation so um I'm just trying to read more okay people are still talking about acing stuff so that's cool okay we're going to go ahead and wrap this thing in a
proxy class so how I would generally do this type of thing is I would make a seal class and this one is going to be simple because HTTP Cent does a buch of stuff we're only using one thing I'm only going to do this one thing and I'm going to basically map the API call so that we're not doing anything fancy it's going to be a direct drop in so how that would look is I would go right to here which looks terrible uh where's the line too many comments holy I would basically take this whole thing out go back to here I would make a wrapper and this thing would have a private read only HTTP client generated Constructor thank you very much so let's do this this is just going to be a pass through so in fact thank you co-pilot literally I'm
not doing anything except proxying the method call to the wrapped class then from there you can't see the context menu I'm Alt Enter gives us the the refactor menu uh extract interface is what I'm picking again you can't see it um I'm making an IH HTTP client I'm going to add it to the current file I don't know why it jumps us to the top like that but you can see it added this interface right here so now we have this that makes the class perfectly testable yeah right so we have that um now what's going to be tricky though I just realized this is not a it's not a solution for us yet and I'm going to explain why in just a moment uh this is an I knew this was going to happen as an oversight from doing a little bit of prep
I was like ah we're going to run into something uh could you please increase font size I can uh let me know if that helps there's only so much real estate on the screen though um okay so I'm going to explain why this looks like it's going to work but it still doesn't work for us um so we're saying now we have the interface but what we would be doing is this the problem is that we don't control like this is stilling up a class we basically need something that can give us back an IH HTTP client so I didn't think this one through but it's going to make our refactoring even more challenging because this part here is still the problem and it's still the problem because we have this essentially right so from here like I have an interface that's great but I
can't I can't mock this one out because it's being created here so I have to go one step further and it would mean that this uh HTTP client Factory that creates a client you can't see my tool tip I realize so when I'm hovering over stuff it's not helping but the create client call because this returns an HTTP client not the interface we kind of need to get one level before this to say no one's giving us back a real class they need to give us an interface for us to work with it does that make sense yeah ites how are we going to solve this I'm not sure yet but I think um we have to have something else and I I'm going to be terrible with names it's essentially the same idea though so we need to be able to do this might
be a breaking API change though for what it's worth um I don't know quite yet so let's see um I can't use this name because this already exists so we could do something like this just to give it a different name and the difference is that I want to give it uh an interface on the return type one of the tricky things that's not very obvious is that create client is a extension method and it uses this default name so what we're actually seeing when we call create client is is actually this so um let's see technically what I would want to do is have this or you do an extension method the same way they did but we're calling this not this one everywhere else now what I would want to do again I'm kind of flying by the seat to my pants right
now I have no idea if this is going to work or not so I apologize but I want to be able to do a proxy Factory HTTP sorry I proxy this one I put a semicolon there we go so we would go implement this thing which basically sorry for jumping around um I zoomed in so it's hard for me to see what's going on now the code here actually let me call all this you would basically do how's this going to work this one just calls the other one because can we get hdb client I want to get the default options oh I guess it's there default name is just an empty string so um this one's just a pass through to the other method and then inside here we have to go do this work yeah then we need to pass that in so
almost I don't want that to be nullable and the way that would all work we're not going to be doing the nullable thing like if someone's making this they're making the real thing so we have this proxy Factory now and what we would want to do good thing we have those tests in place cuz we're about to break some code I have to zoom out a little bit I'm sorry because I can't I can't navigate my own code when it's that far zoomed in um we have to go change this stuff here so depending on when we realize we're about to have this problem right this could be a real scary refactor because we don't want this anymore even though we added it in we need this proxy one so that means up here and here and I think we have it passing in all
the places which means we don't do that I'm doing this and trying to figure out how bad this is going to be in just a moment uh I made it nullable so we could still make it nullable perhaps um if we make okay so here's what I'm thinking if you truly wanted to still support the the older calls and not break the API we have to start forking the code a little bit and it's a little bit gross so you start to introduce Tech like consciously introducing Tech de to say we want to support both ways where someone can basically new up an HTTP client still because they never had this proxy thing in the first place so if you need that we still have to navigate it the right way um otherwise we just kind of skip it so I want to see if
we can it's going to look gross but maybe we can support both and just see what's up it will mean that the Legacy this is important to call out the Legacy calls that do not pass in the proxy one they will still not be unit testable because they would still be using a real HTTP client okay so how is this going to work well I really need to think this through for a second so it's going to come down to posting and what we might be able to do is I'm trying to figure out how we can reuse this code so this is going to get messy for a second can I jump in here and recommend you use monads how would you Jesse if you want to write in the chat how you might expect to see that that would be cool but I
I have an I think what you're about to say is the direction I'm going but I might not have refer to it as the same way so the point is that this code right here regardless of the parameters and stuff I need this signature and that's this signature here so I could do something like I'm going to get rid of this sorry uh what's the return type say task so we could basically have a method or I might do a funk here how's this going to work this is the wrong way to do this sorry um I need to have I want to assign a variable that is a function pointer to one of these two options so either it will point to a real HTTP client's post method or will point to the other one but this syntax is making me lose my mind
here so I need to assign it to one sec that's making me super confused there's too much going on here Jesse budget just wrote out moads again so um okay let me see I want to write the code first that's going to create our clients in the two different ways one is a real client and the other one is not I think once we have that in place we can start to see how this will shape up a little bit better so we don't need the wrapper in one case we will be using our client Factory to create a client just like this and the other scenario is if our HTTP client is null so if this was null right we can't use it so we would have to go make a new HTTP client so you have these two things but we only want
one in one case and the other in the other but they both have this method on them so I don't know why this syntax is so confusing for me up here right now um oh that's because with a funk we have to pass and all this stuff over on the other side so that's why string HTP content which is also nullable and cancellation token as well so we have this method right and we could basically go say if let me just I'm going to write as an if statement that we can turn it into a turn are operator after as far as I know you must create concrete implementation of HTTP context or you have to use third party uh I don't so I don't know if I'm going to have to create an HTTP context yet we'll see I have a feeling I'm not
going to do that because I think I can bypass all of that but it's only going to be in the scenario where we are using our proxy class not for the normal HTTP client so Mo I think we can avoid that challenge in particular but we'll see in just a second so if this HTTP client oh co-pilot actually had it right there no co-pilot if there we go look at that so this is really what's Happening Here right so we can reduce this even further I'm just going to combine this because I want to see if we can make it a turn area operator ah okay so if it's not null I'm trying to just put this all onto the screen sorry now there we go so this this code is wild um but the goal here is that we're trying to still support if
you don't have one of these proxy things you can still call it now again like I was saying depending along the life cycle of when we introduced this if we already had that other HTTP client Factory we would have yet another overload we'd want to do I would hon ly recommend that like this is another indicator if you put interfaces on things you can swap out the implementation entirely um for folks that don't know what I mean by that for this API client if you had a spot that was like a factory that could return an interface to an API client a lot of these problems could go away because you could just make a whole new implementation of an API client and not try to maintain all of the things you'd say cool that one's released it's out there it's in the wild people
are using it we don't love it but we'll go make a new one and it meets the same API spec or a version two of the API and then people can make that one but um that means again if we go back here it's not going to be happy right um what did I call it uh proxy and what doesn't it like oh so this this is where we would use mocks so maybe this is a good opportunity to talk about mocking things so let's assume that we had not released the IH HTTP client one and we went right to our proxy one because we caught ahead of time we said hey look we know that we're gonna have to go mock this stuff out is this all Rob is saying is this all Tam Mo HTTP client I smell carrots yeah this is going
to be a rabbit hole for for sure um this is why I was saying I think that HTTP client you can literally set some things on it that will not go do actual web requests I think but also I would just recommend people design things with interfaces so that I don't have to do this kind of stuff please. net team use more interfaces um we finally got I time provider like thank you um so that you're not trying to simulate time with ridiculous things um okay let's go ahead and I'm going to add mock this is my preferred mocking framework some people hate it because there was some stuff that came out the other whatever two years ago now where they were like scanning repositories and stuff um I don't know I'm not I'm not in it for the drama I'm in it to mock
stuff so let me go ahead and I'm going to private read only mock repository sure uh I make mock Mo with strict mocks so this would be mock Behavior strict up here and then from there uh and sometimes depending on what my test class looks like I will have a Constructor that goes ahead and makes a lot of the dependencies ahead of time but because in this case we're actually looking at different Constructor calls I don't want to go make our system under test one time in the Constructor of the test file uh and by the way with xunit that Constructor runs for each test call so it looks like it's in one spot and it is but it's called every time before every test I don't want to do that here because we actually need the different Constructors to be called so I'm going
to create sorry I don't need to create there it is I proxy there we go yeah so Rob saying I looked at mocking HTTP client at the start apparently you need to instead mock HTTP response Handler and inject that into the HTTP client when you instantiate it so yeah that's I was saying I think there's a way to do it um I thought it was setting things but yeah it's the response Handler that you can mock so that's cool we just took a completely different approach I think this will still work but I think the other way would have allowed us to not go do this second jump to a proxy Factory at least so that's nice so thank you rob for for shouting that out um I figured there was something weird going on there but I didn't know what it was so proxy
object right we'd get that oops and object so now we have those two things pass or on these two tests we have that dependency pass in and we could start mocking things out um so I'm just doing a time check I do have to wrap up in about 15 minutes so Rosario do you from your perspective do you want to go through writing the test for these or was there another aspect if we jump back here that you wanted to see kind of touched on I think we CED all the points I was um that that were pending from from the course I did um I think we are in a really good shape now thanks thank you very much for this cool okay so folks I'm probably not rub saying gross about the HTTP client stuff yeah I'm not going to go through writing
all the tests for these things but what I want you to think about and I'm just going to change up this one let's say so that it's not super gross because I don't this is driving me nuts I don't want to see this on my screen with the weight you do something like this um and what you need to think about is what's going to be called and touch inside of your method right so if we go into log a sync we know that we've created this thing with an HTTP client Factory so it would mean that if we go to run this test in particular this thing should blow up right now and it will blow up because nothing is calling or nothing has set up the mock so just approve it oh come on Visual Studio here we go test fail and it
should say invocation fill with mock Behavior strict all invocations must have the mock like a COR setting setup so there's no corresponding setup sorry there's no create client setup on here so it's telling us it's doing it's doing this we need to go create one of those things so we would go set up that mock to then go return a mock H IH HTTP client and then from there what you could do is you could prove to yourself that you're going to call Post async with the right API URL that you're expecting because that's important because you can check that and if you don't check it someone could pass in the wrong thing maybe your default one's wrong so you can check that your defaults being passed in you can check your payload these are implementation details and people would say don't ever do this
I don't know why if like you would completely skip out on a test like this if you thought it was going to be valuable to because you don't own the endpoint to test against now with what Rob was saying if we were setting up the HTTP client with the other Handler piece that we could mock out we might be able to avoid some of these things and that's okay but the point is that how I would go approach unit testing this is essentially setting up marck and going through that so we don't have to go through that exercise because I think hopefully the the point you can see we're set up to do it at least right so I will wrap up the code part there so I'll stop sharing um but yeah how did how did you find going through that exercise is that
helpful I think it was great uh we started from a really simple at the beginning method and then we went through and I really enjoyed the fact that we were all together and trying to find the right solution rather than you know just watching you coding that that's the real meaning of having a live stream rather than just watching a YouTube video I guess right thank you very much for this of course yeah and thank you for again for reaching out about this because I think one of the one of the things that I I feel like is missing when people talk about refactoring is there's so much focus on the implementation or sorry the behavior don't change the behavior but there's a lot of points missing about who's consuming this thing because when you go to Factor you can refactor and not change the
behavior but all the call sites like you need to be thinking about that too so I think it's just an Overlook thing for sure so another good point another good point you mentioned in the in your course is the fact that sometimes you are ref while you refactoring you are also renaming variables and that could cause breaking change to whatever is you know reverse engineering your code so yeah I think it's a it's a combination of things you need to care about and not as you say not just looking at real Factor itself basically right yeah the the renaming one I'll just mention this briefly I always like mentioning this when people are like the easiest refactor is renaming uh not even a method because sure that could change I was laughing this because this is what I usually do I never thought about this
I just said oh yeah that's true so it's not it's not great practice when you have things that are say like using reflection to go look at uh fields and and things like that but there's certainly code that does this and for good reason too like it it it absolutely exists especially when you're talking about mapping objects and serialization it's not uncommon and people will say the easiest refactor and maybe other people have different opinions but the easiest refactor you can rename a variable right how could you possibly break anything and the the way that you break something is when you have something using reflection or something else that's expecting that name you've just broken that expected Behavior seems weird because you probably don't want to be writing code that depends on this stuff but your context is incredibly important when you're doing this kind
of thing so you need to be aware of all this stuff so um hopefully you don't have situations where you're renaming stuff and it's breaking all your code but it's possible could happen it could happen so again thanks I do apping out thank everyone this was really fun so thank you okay take care folks and we will see you next time e e e
Frequently Asked Questions
What is the main goal of refactoring code as discussed in the video?
The main goal of refactoring code is to change the code structure without altering its external behavior. This means improving the organization, readability, and testability of the code while ensuring that it still functions the same way as before.
How can I ensure that my refactoring does not break existing functionality?
To ensure that your refactoring does not break existing functionality, it's crucial to have a comprehensive set of tests in place before you start making changes. These tests should cover the expected behavior of the code. If you don't have tests, you run the risk of unintentionally altering functionality during the refactoring process.
What are some common challenges faced during the refactoring process?
Some common challenges during refactoring include dealing with existing dependencies that may not be easily changed, ensuring that the refactored code remains testable, and managing the potential for breaking changes in the API that other developers rely on. It's also important to consider how changes might affect other parts of the codebase.
These FAQs were generated by AI from the video transcript.