Flutter TDD Clean Architecture Course [12] – Bloc Implementation 2/2

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
we started to implement the number trivia block in the previous part and you learn the basics of doing test-driven development with streams in this part let's finish the blog implementation so that we can move on to the pendency injection next hello welcome to in cell coder where you are getting prepared for real app development by building better faster more stable apps so subscribe and hit the bell if you want to grow your coding skills we already implemented an important part of the logic which runs whenever the get trivia for concrete number event arrives in the block that's the input conversion however after we have the successfully converted and validated integer for which the user wants to get some amazing number trivia we aren't doing anything with it we are just throwing and unemployment it error so let's change that and be sure to check out the written tutorial from the link in the description where you can also find all of the code written in this video and go through this lesson at your own pace what should happen after we have the successfully converted and validated integer is that we should call the use case get concrete number trivia in order to actually get the concrete number trivia so definitely our naming conventions live up to expectations because like really you can literally see what this use case should do when you call it so again as usual we are first going to create a test for this functionality so we are still inside the get trivia for concrete number group because we are testing by events and whenever this get trivia fork entry number event arrives inside the block after we have tested everything else regarding the input converter the next test will be again arrange act a certain test where the block should get data from the concrete use case here in the arranged part we are again going to return what we've been returning from the input converter the successful parsed number so let's just copy bad for now and with this number successfully parsed because if it's not far successfully the block should immediately emit an error state so in that case the execution of the blocks logic ends there but if the conversion is successful we continue on with the following code and also of course we are gonna have to mock the actual use case so when the mock get the concrete number trivial use case is called with any number then we want to answer because that's actually asynchronous and we want to answer with the right side of either because we want to simulate success and it's going to contain the number trivia which we've created in the last part it's just simply an instance of our number trivia entity right here inside this group and after we have arranged the test we're going to act so we're going to call block that this page we can just copy it from the previous test and we are gonna dispatch get trivia for concrete number with the number string and then similar to how we needed to await until called with the input converter because otherwise the third part would run even before the logic had a chance to be executed inside the block so we need to await until called and this time we're going to wait for the mock get concrete number trivia to be called with any argument and now inside assert we just want to verify that in fact the mock concrete number trivial use case was called now with a precise argument and we are using the params object you pass in arguments to our use cases so we cannot justify that it should be called with the team number now we need to wrap it inside params in the params for get concrete number trivia can contain a number and that number will be the number parsed which is returned from the input convertor so whatever is converted inside the input convertor and subsequent will return from it the same number should be passed into the use case that's what we are testing here this is the simplest test of them all because we are not expecting anything we're just verifying if something has been in fact called so of course this is now going to fail and that's because we are currently only throwing an exception from within the number trivia block whenever the conversion is successful so let's now implement this functionality it's pretty simple we are just going to create a regular method with curly braces or actually higher order function with curly braces to be more precise from which we are going to call get concrete number trivia and pass in the integer which we have over here but of course I have even forgotten that we need to wrap this inside params params number is the integer alright and now when we run the test once again it should pass and surely it does and before moving on to other tests let's refactor our test code a bit because over here you can see that we are marking the input converter to return the successful response the number parsed and we are doing the same over here at the top so let's extract this into its own method which will be located inside the group it will be of course void and it will be called set up mark input converter success and it was simply read turn or actually not return just call this when now we can replace all the occurrences of this long-winded when with our custom method call so let's replace it in the first test here and also in the last test which we've just created so we should rerun the tests just to make sure that our refactoring did not break anything it shouldn't have but just to be safe now of course just simply verifying if the concrete number trivial use case was called is not enough we actually need to display the trivia gotten from this can previous case in the UI using states which are emitted from the block but even before displaying the actual trivia we should probably let the user know that something is going on because of course getting concrete number trivia from the remote API will take some time and we want to show some progress indicator something spinning on the screen to let the user know that the app is not just frozen so which states should the block emit when calling get concrete number trivial use case is successful well it should emit empty first because that's always emitted that's the initial state but then it should emit low dimming state and then load did state and the low that state will actually contained number trivia the user is interested in so let's create an error arrange act as sir test which will say should emit and now the states which we write simply in the square brackets the states will be loading and loaded when data is gathering successfully so data should be gotten successfully so again we're going to mock input converter success and also we're going to mark that the use case also returns successful T number trivia and not some kind of failure so let's just copy and paste it over here actually now we're gonna be testing with streams so we're again going to instead of arrange act assert we are going to arrange a certain later and only then act so let's change this to assert later and here what we want to assert is that the expected emitted States so finally expected is equal to a list of states which should be emitted from the block and again the first state always has to be the initial state so we're going to say empty and also you could specify block that initial state maybe if you like that better but I just like empty better then specifying initial state always it's shorter and then the next stage should be load demon and finally load it and what should is loaded state halt well of course the number trivia which we've set up in the test group all right and now of course we need to expect later and we expected the blocks state stream will emit our values so emits in order and the order of the values is emitted or the states committed will be the expected list and now after we have registered our expectation we can call block that dispatch with the T number string yes we are already used to so we are calling it or dispatching get trivia for country number event now let's run this and is of course going to fail how else so let's go right into number trivia block and implement this we should first return the loading state so we need to yield loading and again similar to how we have to have the failure function be an asynchronous generator we need to do the same for the success function so now we can yield something from within this higher-order function and then this get concrete number trivia returns either so similar to how we store the either inside a variable for the input either we want to do the same for our final failure or Trivium and once we have failure or trivia object which is of type either of course we want to yield a state and currently we are testing for when the data is gotten successfully in which case we want to emit load it but again here comes the power of either in that we have to handle both failure and success so we cannot just somehow circumvent this as I've said in the previous part we have to handle both cases so we want to yield whatever will be returned from the failure or trivia why cannot we specify that intellisense does not want to cooperate this time again or actually it does want to cooperate but I've just forgotten to specify a weight because of course this is asynchronous so pardon me now we can call failure or trivia that fold again and if there is a failure because we are currently testing for success we are just going to ignore failure again by throwing an exception which will be unimplemented exception so let's do this that or unimplemented error as it's properly called so we're going to throw this and if the call was successful so if right we're going to receive the trivia object over here in which case we want to simply return load it and pass in the trivia s trivia argument and so we return loaded state from the fault method which in turn is yielded from our acing generator and so on up the chain eventually it's gonna get into the stream of number trivia states so now running the test should pass and now we can move on to what should happen if the call to get concrete number trivia returns some failure in this case those failures can be cache failure or server failure so we're going to create another test in fact it's going to be so similar to the last one we've created for the success that we were just going to copy and paste it below and this time it should of course return loading still because even when we get some error from the use case it still happens asynchronously so we have to show some kind of a loading indicator to the user and then we are gonna want the error state to be present and this is gonna happen when getting data fails all right so now we are still going to mock input controller success because I mean we're going to get to the point of calling the use case only when the input converter actually is successful because if it's not we are immediately going to yield error so on if it is successful can we get to calling the use case here but what we want to change is that the get concrete number trivia mock will not answer with write T number trivia but instead left and let's do server failure so we need to import that over to this file and now we should assert that the state's emitted are not empty loading and loaded but instead the last state will be error and here it's pretty important to specify a correct message so because we are omitting or getting server failure from the use case the message should be server failure message which we have created in the last part this is simply the string constant and all of the error code of this test can remain as it is so let's now run this and it's going to fail of course so let's go to number trivia block and instead of throwing unimplemented error from the failure side of either we are going to return error and pass in the server failure message as the message so let's run this again and this time it passes but of course the problem here is that we cannot just simply put the server failure message always into the error what's gonna happen when the get concrete number trivial use-case returns a cache failure then there should be the cache failure message right but something like this is currently not in the tests so let's create a test they will make sure that only the proper message for the failure is inputted into the error State and it's very important to handle all of the possible types of failure inside the block because it's important to show the user a precise and meaningful message it's not enough to just handle the base class failure and call it a day we need to handle every single subtype so let's do that right now we're going to just copy and paste the test below and we're going to call this that should emit loading error yes with a proper message for the error when getting data fails all right that's quite a long name for this test but anyway instead of sewer failure we are going to return cash failure from the concrete number trivia use case and then the message which should be there in the error state will be cache failure message so now let's run this test again and it's going to fail so now inside number trivia block we're going to spawn a monster here but don't worry we're going to fix it in just a little while that's because just to make this as simple as possible we are doing TDD after all so we are just writing the least amount of code possible for the test to pass we're going to put a ternary operator in here so if failure is server failure we are going to poops return the server failure message of course we need to import server failure or failures that dart file to be more precise and otherwise we are going to put the cache failure message into the error State so this code is definitely not good code is not even readable very much but anyway this should make the test pass and only then later on we can go on to the refactor phase because remember with test-driven development we are doing read test fails green test passes and only after the test passes are we going to really refactor some exceptions may apply but mostly that's what you should be doing with TDD in addition to this being unreadable this ternary operator is actually quite unsafe because what if the failure type is not a server failure nor is it a cache failure of course we do not have any kind of utter failure which can happen inside here still if we had some unexpected failure here the message would still be the cache failure message which is not cool that's because this last part of the ternary operator is basically an else clause so now to make this safer and also more readable let's extract this ternary operator into its own method which will be private inside the block we're going to call it map failure to message and it's of course going to return string it's going to accept the failure and sometimes I wish that the darts extension methods were already here because we could do it really nicely with an extension method which would be local to this class but still they're just working on it so we have to create these kind of stupid helper functions as of now but anyway we are going to switch here and we could of course do if failure is and specify server failure and so on but we can also do it with a switch statement I think it's nicer the way we can do it with switch is that we're going to switch over the failures runtime type and now we're going to provide cases for this switch so in case that the failure is server failure we're going to return server failure message in other case when it's cache failure we're going to return cache failure message and finally sadly dart still doesn't support any sealed classes or tags unions or some types or however you want to call it so we have to handle the case when the failure will be none of these and we're going to handle it with the default clause and in this case we are going to return a string called unexpected error because it's really unexpected that this is going to be even returned because there is no even possibility of having a different type of failure test into this map failure to message method after all and even though the dart team as of speaking now is already working on adding sealed classes or something similar to Dart if you want to learn how to work with sealed unions which is a package fixing up missing darts functionality check out the video from the cart in the corner and now of course we are just going to call map failure the message instead of this ugly ternary operator and pass in the failure and that's basically all there is to do for us so now because we have refactored the code we need to rerun the tests of course and they should still pass because we have not changed anything extreme and now with this we have successfully implemented the whole logic which will run whenever the gate trivia for concrete number is dispatched to the block but there is also another event and that is get trivia for random number and as we are already used to the random event when that is triggered will be very similar to what happens when the concrete event is triggered but with one exception it's going to be much simpler because we've get trivia for a random number we do not have to handle input conversion because there is no input it's just a random number generated over in the remote API so because of that let's go over to the test file and copy we can actually copy the whole group for get trivia for concrete number so copy it paste it below we're going to again break the methods of test room and development for the sake of us not getting crazy because these implementations are really similar but in addition to just renaming things to make them work with the random if and calling the random use case and so on we can actually delete a lot of code in here because the input conversion and validation will not happen so we can remove the test should emit error when the input is invalid and also the first tests which says that it should call the input converter so we can delete the first two tests and of course we'd no longer need to have input converter success here because input converter will not be called from within get trivia for random number so was renamed that and also because we are not validating any input we are now going to need T number string North T number parse because they are not needed we just need the T number trivia because everything else is basically just generated inside the remote API we do not care about the inputted number so now let's fix up all of these tests one by one and then we are going to implement the whole functionality of the random event inside the block after we've modified these tests so the first test should get data from the random use case we're now going to mark input converter so the use case will be market random number trivia and the random number trivia use case does not take in any parameters so what we've done is that when something does not take any parameters and it's a use case we can specify no params right and of course we would also need to import no params over here but because we are just marking it we can still just pass in any you check for no params only in the verification part the event which should be dispatched is of course get trivia for random number which does not accept any arguments we're going to wait until get random number trivia use case was called and then get random number to Shiva use case and we want to verify that it was called with no params now we're gonna do the same thing for the method below so for the test below so should Emmitt loading load it when the data is gotten successfully that's correct that's what should happen also from the random event because the only thing that changes is that we do not convert the input so let's copy the mocked random number trivia use case from above the expected states can remain the same and so expect later can remain the same as well and this patched event should again be get trivia for random number and that's about it for the changes to this method next again we're simply going to just mark the random number trivia use case again and dispatch the random event so let's copy and paste it and of course you can get all of this code from the written tutorial and over there you can also get the whole project so definitely check it out from the link in the video description lastly even for the last test we're going to do the same thing so mark the random use case without the input converter and dispatch the proper random event and once we've done that we can now run the test code and it's going to fail of course and we're going to get to number trivia block and we're going to do the simplest thing possible and that is to create an else if and we're going to check if the event is get trivia for random number and here we are simply going to copy and paste the code from above after integer was successfully validated and converted so just copy it from within this integer higher-order function and paste it into this event handler so to say so we're going to yield loading and then instead of get concrete number trivia we are going to call get random number trivia and basically the only other change which will happen in here is that we're going to call it with no params so it's also import use cases Dart and with that we can now run the tests again and they should pass this time well that's interesting that they do not all pass let's take a look and what happens should return empty loading an error because we are in the test for testing the errors but instead it returns empty loading and loaded oh that's right we are returning right side now the left side even from the error tests so here you can see why test-driven development done the right way is awesome but doing test-driven development with just copying and pasting code is actually pretty bad it's still better than if we did not have any tests but still we've made a mistake in the test just because we were not following TDD precisely we are going to return at the right side of either but left side of course which will hold the server failure in this case and in the test below it's going to return left with cache failure and with that we can now run the tests again and they should test this time all right so now it's time to remove the code duplication because there is not a lot of it actually inside the block but still we can do better if you take a look at this code what we are doing in both of the fun or both of the clauses of the if statement is that we are yielding failure or trivia that fault and then we yield either or return an error or a loaded state so let's extract this functionality into its own method we are going to highlight it and say extract method let's call it it will be private so underscore either loaded or error state and it's going to generate us a method but of course it's return type is wrong for some reason so we have to change it manually and also add a comma so that it will be formatted for us and we need to change the void return type to be stream of number trivia States and after we make this method to be an asynchronous generator so aceing asterisk we can now yield from it failure or trivia and return error or loaded and now we can call this method from both of the clauses of the if statement but we should also yield each so you'll asterisk and we are of course going to do the same for the random event and with that we can now run the test again because we've refactored the code and they should still pass and they do so now we have the block fully implemented it has its dependencies in place we are calling them from within the map event to state and we have it fully tested and the code is looking pretty decent and also because we are using the either type from the darts package we are forced to handle errors or failures even though we might not want to handle them we just cannot go around that unless we are really clever but of course you can always go around something if you really want to but it's much easier to go with the flow where you have to handle the failure and at least you are definitely not going to forget about it as you would if you were just using regular exceptions all over the place to go through this tutorial at your own pace once again and also to get all of the code check out the written tutorial available from the link in the description we're getting close to the finish line because now that we have literally every bit of logic implemented it's time to put all of the parts together with dependency injection in the next part and then well there will be nothing more for us to do than to create the user interface so if you do not want to miss those parts and also more tutorials like this definitely subscribe to this channel and also join notification squad by hitting the Bell button to make sure you grow your flutter skills because here on whistle coder I am determined to provide you with the best app development tutorials and resources out there if this video helped you with implementing the block finally give this video a like and also share it with our developers will surely benefit from it too leave a comment if you have anything to say in suggestions or questions and Internet [Music]
Info
Channel: Reso Coder
Views: 11,070
Rating: undefined out of 5
Keywords: resocoder, tutorial, programming, code, programming tutorial, clean architecture, software development, flutter clean ui, flutter clean architecture, flutter clean code, flutter testing tutorial, flutter tdd, flutter test driven development, flutter crash course, flutter course, flutter, flutter tutorial, flutter dependency injection, flutter state management, flutter bloc library, flutter unit test, flutter mvp, flutter mvvm
Id: YSNeS5S5Nqw
Channel Id: undefined
Length: 33min 49sec (2029 seconds)
Published: Mon Oct 14 2019
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.