مرور بازنویسی کلاینت توییتر0:00
All right, it's refactoring time again. So let's take a look at our last test, which we wrote in the last video. I actually would say this already looks pretty good. We don't have any static text here, which we normally try to refactor. So yeah, I think this test is pretty clear to me, and there's actually not one thing that I would change. But there is something else that I noticed. Maybe you've noticed too, so we have created two new files, TwitterClient and TwitterFacade, and they both live under the app directory.
TwitterClient and the TwitterFacade, and they both live under the app directory. And yeah, I often just create files while implementing a feature and don't think too much about where I put them because I want to concentrate on the implementation and on my test. And then after refactoring or while refactoring, I want to think about where to put them. And normally these external things or these classes that deal with external services, I try to have inside a services directory.
Reorganizing Service Classes0:56
that deal with external services, I try to have inside a services directory. So let's change this now. We can move this class with PhpStorm to a new folder called services. And let's also put this inside a Twitter directory. And if we check this out, yeah, now we can see this here. And let's also do this now for this class here, move to services, and here we already see our Twitter directory. Okay, they are both here now. Let's run our test just to make sure that we didn't break anything.
Fixing Autoload Namespaces1:28
Okay, they are both here now. Let's run our test just to make sure that we didn't break anything. But I think we should be fine. And we are not. Let's check this out. I think this could be because of the change of the class name or namespaces. So let's run composer dump-autoload. And I think this should already do the trick so that these files are now being found. And yes, they are.
Reviewing ServiceProvider Binding1:52
so that these files are now being found. And yes, they are. All right, good. Okay, what's next? So we created also ServiceProvider. Let's take a look at that. So what we're doing here, we are creating a new Twitter object with all the credentials. And when someone asks for Twitter, we are returning a new Twitter client with the Twitter instance inside.
And when someone asks for Twitter, we are returning a new Twitter client with the Twitter instance inside. So this is what we do here. And then inside our test, let's check this out again. Here at the top, we're going to fake the Twitter service. And this is how we get our fake Twitter client. But there is one thing that I don't like about it. So if we forget to fake the Twitter service, then inside our test, we're always going to end up using the real Twitter client.
Preventing Real Client Usage2:32
then inside our test, we're always going to end up using the real Twitter client. And this can be an issue because, yeah, you can easily forget to fake the service. So I'd like to have some kind of security wall here just to prevent this. So what we can do is inside our ServiceProvider, I can check if we are in the production environment. We have here access to the application. If we say we want that here, let's give us the location.
We have here access to the application. If we say we want that here, let's give us the location. It's this one here. Now we can say if the app environment, the current one, equals production, what do we want to do then? Then we want to return the TwitterClient, which we just created. But if we are not in production, if we're locally, then actually we never want to use the real TwitterClient.
But if we are not in production, if we're locally, then actually we never want to use the real TwitterClient because then mostly we're using it for our tests. And there we don't want to send out anything in Twitter. So what I like to do in those cases is return a new NullTwitterClient, which we don't have yet. And this is just a class which is similar to TwitterClient, also with a tweet method. But actually the tweet method is just doing nothing. And this way we are secure also when we're not in production.
Adding Null Client Interface3:51
But actually the tweet method is just doing nothing. And this way we are secure also when we're not in production that we don't use the real Twitter client. All right, so let's create this one. Let's do this also inside here. New class, let's call this NullTwitter, Null because it just does nothing. And we also want to have here the same tweet method, but since we're now having multiple clients, it's good to also use an interface.
but since we're now having multiple clients, it's good to also use an interface. So let's think about what could this be. Let's call this TwitterClientInterface. And let's create this class also here on the Twitter. And the only thing here which we need is the tweet method. So this will be a public function called tweet, and we're going to return an array. So I guess that's the only thing we need here. Now we need to add this method here from our TwitterClientInterface.
So I guess that's the only thing we need here. Now we need to add this method here from our Twitter interface. And here we're just doing nothing except returning an empty array because we have defined that we need to return an array. Okay, so let's also add this to our TwitterClient here. Implement TwitterClientInterface. And yeah, here we have defined that the status should be a string. So this means we should also do this now here, string status. And I think with that, we should be fine inside a TwitterClient. Yes, we are.
And I think with that, we should be fine inside a Twitter client. Yes, we are. So with this interface, we make sure that maybe sometimes we're going to add some more methods to the TwitterClient. And then with the interface, we're just making sure that all our different TwitterClients, the real one and the null one, have this same public methods. All right, and then inside our provider, we can now import this class. Let's run the test again.
All right, and then inside our provider, we can now import this class. Let's run the test again. They look good to me. Yes, all good. And now we have made sure that if we forget to fake the Twitter service inside our test, we can still be sure that we don't send anything out because we're using a null Twitter. You might say we could also define for all our tests that we use our fake Twitter service. So let's go back to the test here.
that we use our fake Twitter service. So let's go back to the test here. So we could define this inside our TestCase, but I prefer to still have this inside my test because it reads really well what we're actually doing. We're faking the service, and then we're making some assertions on the same service. So I prefer this over having this inside my TestCase. And then inside the test, I don't really know what's going on. So I like to be very clear about what we're doing here.
Binding via Interface Keys6:31
And then inside the test, I don't really know what's going on. So I like to be very clear about what we're doing here inside every test of mine. All right, so now the only thing left, let's go back to our provider. Since we now have an interface for all the different clients which we use, we can also use the TwitterClientInterface here instead of this string here because then if we change the class, we don't have to change this everywhere we're binding to this Twitter string or to this facade accessor key. So let's use the TwitterClientInterface class here.
or to this facade accessor key. So let's use the TwitterClientInterface class here. And I believe we also have to do this now inside the facade. Yeah, here as well. So let's also use this class here. And again, we're running our tests, and they also should be fine. But yeah, this is a nicer way because again, if we want to change the name here of this class, for example, then this is being changed anywhere. And this cannot be done with a static string like we had before.
then this is being changed anywhere. And this cannot be done with a static string like we had before. All right, so I think this looks good to me. We've made sure that we have now this new directory, we have an interface, and we have this nullTwitter just to make sure that we don't really send out anything to Twitter through our tests if we maybe forget to fake the service.
