Flutter TDD & Clean Architecture - Learn By A Project | Full Course for Beginners [Tutorial]

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
what's up guys I came to tell you that we are back we are back with a new clean architecture project without wasting time let me tell you what we are going to do but first of all I want to say thank you for the energy you gave me in the previous video and if you are a new member and want to learn clean architecture I recommend you first watch the previous project and then come here well let's go to the project I want to say briefly in this project we will build a simple application which user enters the name of a city and then we will show the temperature of that City to the user with the help of a public API well maybe now you want to tell me what is the difference between this project and the previous one I have to tell you that we will use test driven development in this project that's why I chose a simple project for clean architecture and I want to focus more on test writing before starting let me give you some great news from now on you can join our flutter community on telegram simply join this group to stay in contact with other flutter developers to learn from each other and to become a better flutter developer so don't forget and join now now it's time to go to vs code first let's check the packages that we're going to use for State Management I always prefer to use block because it is simple and highly readable and most importantly it is structured also we will use darts to specify success and failure and for dependency injection I want to use get it we will use equatable to compare States and HTTP to request the server the reason we don't use do or retrofit is because we will focus more on writing tests in this project probably because some of you wanted to learn test writing before you are familiar with some terms or packages so you may know that most of the time we use three packages for testing that we have to Define in Dev dependencies and they are flutter test makito and mocktail and because I want our test coverage to be high percentage we will also have UI test most of which includes the block test so we also need block test to have in the dev dependencies so this is all the dependencies we need in this project well let's create the folders as you probably know in our lib we will have three folders named presentation domain and data in the previous project these three folders were placed in the feature folder but since we have just one feature in this project we don't need the feature folder anymore well guys this video was a brief introduction I just wanted you to know what we are going to do stay with me in the next videos welcome back guys guys I don't like to waste time so let me give a summary of what we will do in this part in this part we will start coding and the first thing we will do is Define entities I'm saying again if you haven't seen the previous clean architecture project please watch it first then come back to this video because in this project we are not going to focus on clean architecture and I might use terms that you don't know so watch the previous video first as I said the first step is to define the entities after that we continue by writing code for weather Repository the weather repository is an abstract class and will be implemented later at the data layer then we will write code for the use case named get current weather and finally we will implement the use case test so let's get started as you may have understood we have to start from the domain layer why the domain layer because this layer is a layer that does not depend on any other layer so it will be safer if we start with this layer domain layer has three parts entities use cases and repositories we start by writing the weather entity but first let me create the folders now in the entities folder I create a file called weather as I said this is a simple project that's why we only have one entity named weather before we Define The Entity class let's check the API that we are going to use I want to use open weather map in this project you can also register for free and get a token for requests to the server we will use this URL where we will pass the city name and the API will return information about the weather of that City we will use these fields for the weather entity Fields so I Define the required Fields but before that in the weather file I first define a class called weather entity and then Define the fields also I have to use equatable to compare the states that we will do in Block later that's why I extend equatable and then I set the fields that I want to compare in the props well the definition of weather entity is finished the next thing we need to do is to create the weather repository for this first I create a new file called weather repository as you may know whether repository is an abstract class and will be implemented later at the data layer now you may have a question why should we Define abstract the domain layer should be completely independent of all other layers but how can it be independent when any use case obtains data from a repository that is shared with the data layer now comes the art of dependency inversion one of the solid principles in fact in the domain layer we create an abstract repository class to define a contract for what the repository must do and then in the data layer we write its actual implementation well let's continue in this class we also have a future method called get current weather this method has a string parameter called city name also its return type is either if you don't know what either is don't worry let me explain either is a class that we can use to handle errors it has two generic parameters named right and left in right we set the value that we want to return when our operation is successful and in left we set the value that we want to return when our operation is not successful when the operation is successful I mean when we make a successful request to the API and the API Returns the response in this case we return the weather entity so I set the weather entity on the right and for left we need to Define an abstract class called failure in this class we have a string field called error message in which we put the relevant error text to do this first I create a new folder in lib called core and inside core I create a new folder called error and then in error I create a new file called failure now here I Define the failure abstract class we can have various errors during the application such as server failure or connection failure or even database failure that's why I want to add these classes now we have to go back to the weather repository file and set the failure class in left well we are done with this file now it's time to go to the use case definition to start I create a new file called get current weather in the use case folder as you may know the use case as Uncle Bob mentioned orchestrates the flow of data to and from The Entity a use case in its most basic form represents a user action such as retrieving weather data by City retrieving weather information by coordinates and so on to be compliant with the single responsibility solid principle each use case must be independent of the others now here we first define a class called get current weather use case then here we must have a future method called execute in this method we must use the weather repository and call the get current weather method so first I Define an instance of the weather repository class then I change the return type of execute to the return type of the get current weather method in the repository I mean to either then I return get current weather the reason for this error is that because the get current weather method has a string parameter and we need to pass a string that is the city name so the execute method should take a parameter called city name and then pass it to get current weather well we have done everything to write the first test and now we have to start writing tests but before starting let me explain about test writing the workflow of the application development process with tdd is as shown in this diagram note that the tdd process is iterative and it's called the red green refactor process so now let me explain these steps the first step is write testing scenarios which is the red part the development of a feature begins with writing a test scenario first writing test scenarios usually follows the feature requirements in a PRD document in the test scenario there will usually be a flow of the developed features such as determining the data source ensuring the incoming data from the API produces the appropriate model designing the state flow of the view based on the data and so on the first time we write a test scenario we will get errors this is normal because the feature code does not yet exist guys we didn't do this part the correct process is to write test then Define the use case but because I wanted you to get a little familiar with clean architecture and test writing I first created use case from the next videos we should first start writing tests and then write code the second step is make the testing scenario successful which is the green part in this step the actual feature code writing is done writing code at this stage does not have to be neat and optimal because the main goal in this step is to make the testing code that has been made previously successful the third step is refactor the code after the testing code has been successfully run without any errors it is time to tidy up and optimize the code that has been written both testing code and actual feature code well now it's time to start as you probably know everything related to testing should be placed in the test folder this widget test file that you can see is a default file that is created when the project is created and we can delete it guys please pay attention here in the test folder we have to create exactly the structure we have in the lib folder this means we have to create data domain and presentation folders so I do it because there is nothing to write tests in the core folder I did not create the core folder also as you know we have to create the use case folder in the domain folder in the domain folder we only want to write tests for the use case so the entity and repository folders do not need to be created now here we have to create a file with exactly the same file name as in the use case folder and we have to add a test to the end of the file name this means that now we have to create a file called get current weather test well now here first we have to define a method called main all the tests that we are going to write must be written in the main method also when we want to run the test this main method will be executed so first I Define a method called main as you know we want to write a test for the get current weather use case so we need to First create an instance of the get current weather use case class let me ask you a question what do we want to test we want to ensure that the repository is actually called and that the data simply passes unchanged through the use case well according to this answer we need the weather repository here as well and as we know the weather repository class is a contract writing a contract of the repository which in the case of Dart is an abstract class will allow us to write tests for the use cases without having an actual repository implementation now the question is how can we test an abstract class testing without concrete implementation of classes is possible with mocking a popular package for this is called makito which we've added to our project as a Dev dependency so we have to create a mock weather Repository to do this first we create a folder called helpers then inside it I create a new file called test helper to create a mock weather repository we must use the generate MOX annotation from the makito package now here we have to enter the name of the class we want to mock which is weather repository there is also a mock for the HTTP Library so that it can simulate requests to the API during testing and we can add it like this now to generate mock file we need to run the following command via the terminal as we can see the file was generated now it's time to go back to the test file and continue as we said now we have to Define an instance of weather repository but instead we have to use mock weather Repository mock weather repository is the generated mock class which implements the functions inside the weather repository to operate with this mock weather repository instance the get current weather use case will get it passed in through a Constructor tests in Dart have a handy method called setup which runs before every individual test this is where we will instantiate the objects so we must initialize all the objects we need in the setup method now comes the time to write the actual test to write and run the test we must use the test method in flutter in this method we have two parameters named description and Body in the description we should write what we expect from the test for example this test should get current weather detail from the repository we have an instance from the repository right now but what about weather detail we need weather detail to test to see if use case gets the weather detail from the repository or not so I Define a variable called test weather detail that is equal to new instance of weather entity now we can set any value we want in the weather entity and it doesn't matter we also need to Define a variable named test city name that we passed to the repository and use case let's continue the arrange act assert pattern has become almost a standard across the industry it suggests that you should divide your test method into three sections arrange act and assert each one of them only responsible for the part in which they are named after now in the arrange phase of this test what we want to do is to provide some functionality to the mocked instance of the repository here we can use when method this is really nice the syntax that is here because you can really read it as an English sentence and it will all make sense so when mock weather repository get current weather is called on it with any argument like test city name then answer because it's asynchronous so you cannot call then return but you have to call then answer and the answer will be asynchronous and what do we want to answer the return type of get current weather is an either and the right side of the either type is weather entity and the left side is failure in the test it doesn't really matter what we return we can return really either failure or the weather entity in this case let's return the weather entity so we want to return the right side of the either type now let's move on act phase act steps should cover the main thing to be tested this could be calling a function or method calling a rest API and so on keep actions focused on the target Behavior so in the ACT phase we must call the execute method in the get current use case and save its result in the result variable and finally in the assert phase we verify whether the unit behaves as expected we may expect a method to be called or the result to be the same as the expected result for this we need to use expect method so we expect that the result of the execute method on the use case will really be the same thing as was just returned from the mock weather Repository so we expect the result to be right and inside of it we expect that there will be test weather detail present and finally we did it guys writing the test for get current use case is finished now it's time to run the test for this we can use the testing section in the editor now here we can click on run tests and the test will run as you can see if the test runs successfully we will face this this tells us that the test with this description was run successfully guys that's enough for this part stay with me in the next part because the next parts are much more important welcome back guys I hope you are well and enjoyed the last video If you haven't seen the previous video please watch it first in the previous part we worked on the domain layer and wrote tests for the use case we can now move on to working on the code at the data layer at the data layer there are data sources models and repositories so first of all let me create the folders inside the data layer [Music] wait a minute is there a repository at the Domain layer 2 yes the repository at the Domain layer is in the form of an abstract class in which there are functions that still need to be implemented so it is in the repository at the data layer that we will start implementing the abstract class let me explain something now method return types of the data sources will be very similar to the ones in the repository but with two huge differences they're not going to return the errors in line by using failures instead they will throw exceptions also rather than returning weather entities they're going to return weather model objects all of this happens because data sources are at the absolute boundary between the nice and cozy world of our own code and the scary outside world of apis and third-party libraries models are entities with some additional functionality added on top in our case that will be the ability to be serialized and deserialized to and from Json the API will respond with data in a Json format so we need to have a way to convert it to Dart objects you may have noticed that the weather model will contain some Json conversion logic this word should start flaring red lights in your head because it means employing test driven development again let's look at the tdd diagram again the first phase is to write the error test we are going to write the test before writing the production code this ensures that we won't add a bunch of things that we don't need and we'll also get the confidence that our code isn't going to fall apart like dominoes so let's get started as you know the folder structure in the test should be exactly like the lib structure so the first thing we should do is create data source models and repositories folders and data now we need to create a new file inside the model called weather model test like the previous test we first Define a method called main all tests will be written inside the main method here we will test three main things is the model that we have created equal with the entities at the Domain layer does the from Json function return a valid model does the to Json function Returns the appropriate Json map so first we have to test that is the model that we have created equal with the entities at the Domain layer to start as we did in the previous video we need to use the test method it takes two parameters description and Body in description we write the description that this test performs and in body we write the code we can write in the description should be a subclass of weather entity but what should be a subclass of weather entity the answer is weather model so here we need a weather model and that's why I Define a variable called test weather model which is equal to new instance of weather model we have an error at the weather model this is normal because we haven't written the actual code yet this means that we have completed the first phase in the tdd diagram and now we have to go to second phase in the second phase we have to make tests successful so now it's a good time to start writing the production code to do this we need to create the weather model class so we create a new file in the models folder called weather model the weather model class will extend weather entity class and simply pass all the Constructor parameters to the superclass now we go back to the test file and import the weather model and then we can set any value we want in the weather model and it doesn't matter in this test we do not have the arrange and act phases and only have the assert phase as you know we use the expect method in assert which is very simple in actual we set the result which is equal to the test weather model and in matcher we verify whether the result is matched with the weather entity using the is a method now we can run the test as you can see the test passed successfully and there is no problem this means that weather model is a subclass of weather entity let's go to the next test in this test we are going to test from Json method from Json method should return a weather model instance with the same data as is present inside the Json string we are not going to get the Json string from the live weather API instead we will create a dummy data which is just a regular Json file used for testing that's because we want to have a predictable Json string to test with for example what if the weather API is under maintenance we don't want any outside forces to mess with the results of our tests to create dummy data I first create a new folder called dummy data in helpers then inside it I create a new file called dummy weather response in Json format here we will have only a simple Json that the API returns for example I sent a request to the API to return the weather details of New York City and now I just copy this Json response and then paste it in this file now we have to read this file in the arrange face for this we can use Json decode and read it using the read Json method in the path of the Json file and save it in the Json map variable we have an error here because we did not implement the read Json method to implement it I create a new file called Json reader in the helpers folder then I create a method called read Json which return type is string and inside it I read Json using file methods and return it now go back to the test file and import Json reader now we can move on the ACT face in this phase we must use the from Json method in the weather model class to convert the Json into a weather model and store it in the result variable let's implement the from Json method in the weather model file we Define find a factory method which its return type is weather model then we map the Json and store it in the fields this is simple and no need explanation let's go to the assert phase in this phase we expect that the result returned from Json is equal to the test weather model so let's run the test and see the result the test passed successfully this means that the from Json method returns a valid weather model and in the last step we have to write test for to Json method guys we will not use to Json method but we do this for more practice because it may be useful in your projects in this test we do not have a range phase in the ACT phase we use to Json method from the weather model this method converts the model to Json and we store this Json as map in the result variable now let's define to Json method in the weather model this method is very simple like from Json now in the assert phase we first need to Define a variable named expected Json map and set its value to this Json just note that the values of this Json must be the same as the values of the test weather model that we defined and finally we expect that the result returned by to Json method is equal to the expected Json map now it's time to run this test and see the result as you can see test passed successfully this means that to Json method is working well and there is no problem so guys in this video we wrote tests for all three scenarios and it can be said that we tested the model class and made sure that there is no problem in the next video we're going to test the API so stay with me what's up guys I hope you had a good day and enjoy third part in the previous part we created the weather model and fully tested from Json and to Json methods in this part we move on data sources and we test getting response from API this part is important so stay with me until the end to start we need to create a new file called remote data source test in the data sources folder now here as you know the first thing we have to do is to define the main method first of all because we are going to send a request to the API we must have an object from the HTTP client if you remember in the second video in the test helper when mocking the weather repository we also added the mock HTTP client this mock is for the HTTP Library so that it can simulate requests to the API during testing if you don't know what mock is let's go to flutter's document it is very easily explained here sometimes unit tests might depend on classes that fetch data from live web services or databases this is inconvenient for a few reasons for example calling live services or databases slows down test execution a passing test might start failing if a web service or database returns unexpected results this is known as a flaky test it is difficult to test all possible success and failure scenarios by using a live web service or database therefore rather than relying on a live web service or database you can mock these dependencies mocks allow emulating a live web service or database and return specific results depending on the situation therefore I Define an instance of mock HTTP client so that we can use it to simulate requests to the server in the next step I will define an instance of whether remote data source implementation class this class will extend abstract class called weather data source and will implement the method we defined to request the server inside abstract class we have an error here but that's normal because we haven't created the weather data source class yet in order to be able to compile and run the test we need to fix this error so let's create the classes for this I create a new file in the data source called remote data source here now I Define an abstract class called weather remote data source and I Define a future method called get current weather which takes a parameter called city name and its return type is weather model this method is a contract the reason we use abstraction is because it makes testing easier now in the next step I define a class named weather remote data source implementation that extends weather remote data source class as you know in this method we will send request to server that's why we will need HTTP client so I Define an instance of HTTP client which accepted by class Constructor this error tells us that we should Implement get current weather method so we Implement method to fix this error like this now let's go to testing code to continue later we will return to this file and Implement request to server first to fix this error I import remote data source file now we have to initialize instances as you know we can use the setup method and initialize now it's time to test we have a method called group that we can put several tests that are related to each other inside that group and run that group for testing so since we are going to write two tests I want to use group method like this we will test the process of getting weather data from API and we have two conditions returning a valid model when getting data is successful and returning a server exception when getting data is failed so we must first test when response code is equal to 200 this means that getting data from server is successful and it should return weather model before anything in order for the project to be clean I create a new folder called constants in the core folder and inside it I create a new file and then define a class called URLs and I Define URLs and paths and also API key inside this class this test has three phases namely arrange act and assert in the arrange phase we will use when method we have to stub the data to make sure mock client Returns the right response data when we call the get current weather endpoint in flutter stubbing means returning a fake object when the mock method is called for example when the test calls the get current weather their endpoint using mock client we should return a response object with status code 200. so here using when method we have to return a fake data inside the when we send requests to API with current weather by name endpoint using mock HTTP client as I said stubbing a mock can be done with the function when which can be used with then return then answer or then throw to provide the values needed when we call the mock method the then answer function is used for methods that return a future or stream while then return is used for normal synchronous methods of the mock class now using then answer we should return a response object with status code 200. now we need to return fake data with status code 200 for fake data we can use dummy data that we created in the previous video like this fix this error related to city name here I Define a variable named test city name and set its value to New York and then pass it here now we can move on act phase this phase is also very simple in this phase we have to do the main action that is we need call get current weather method in weather data source implementation class and save result in the result variable and in last phase we just need to use the expect method to check the result returned by get current method is a valid weather model or not and finally I run the test but unfortunately test was not passed as you can see the error description says that the get current weather method is not implemented so we need to implement this method as you know in this method we have to implement requests to the API and this request is really very simple and we can do it this way here we check if the status code is equal to 200 it means that getting the response from the server was successful return the weather model and otherwise throw server exception and I run the test again and finally as we can see test is passed successfully now we can go to next test that means we have to check the second condition what is the second condition the second condition is that returning a server exception when getting data is failed this test has exactly the same steps as the previous test that's why I copy the previous test and paste it here and then change its description to this one and now in a Range instead of returning status 200 we return 404 and instead of Json we return an error text like this we don't need to change in the ACT phase so we can move on assert phase in the assert phase instead of expecting to have a weather model we should check that the result is a server exception error now I run both tests and see the result as you can see both tests were run successfully and there is no problem in this video we tested both conditions that we may have in getting data from the server in the next video we will implement the repository so stay with me welcome back guys I hope you enjoyed fourth part and learned API calling testing in this video we are going to work on whether repository implementation and finish domain and data layers testing so without wasting time let's get started as you have learned the first thing we have to do is to create a new file in the repositories called weather repository implementation test as you know here first we need to Define main method for testing we need to access the data source so we need generate a mock for the weather remote data source that we created earlier to do this it is enough to add the weather remote data source class in the test helper then run this command to generate a mock from the weather remote data source now we can go back to the test file and Define an instance of mock weather remote data source we also need to Define an instance of whether repository implementation class so I Define it like this but here we have an error which is normal because we haven't created this class yet in order to fix this error and be able to compile test we need to create this class to do this as I said before we Define repository class in the domain layer but we have to implement it in the data layer so in the data layer and the repositories folder I create a new file called weather repository implementation now here I define a class called weather repository implementation which extends the weather repository class and as you probably know because we need weather remote data source we have to Define an instance of it which is also accepted by the class Constructor this error tells us that we should implement the get current weather method that we defined in the weather repository class here so I create the method then implement it like this this is very simple we get data using get current weather and store it in result and then we return it using the write method in either and if server exception occurs while getting data we will return server failure using left method and if socket exception occurs we will return connection failure now we can go back to the test file and import the weather repository implementation the next step is to initialize these objects and as you know we have to use the setup method like this before we start test writing we have to do one more thing and that is to Define an instance of weather model and weather entity because we will need it later so I Define them like this to start let's first check how many scenarios we have I think we have three scenarios that need to be tested the first scenario is returns current weather data on successful API request the second scenario is return server failure when the request to API fails and the third scenario is returns connection failure when not connected to the internet so let's continue because all three tests are related to each other we can use the group method and write the tests inside it so I use group method and set its description to get current weather now let's move on first test as I said the first scenario is that we have to test that get current weather should return current weather when a call to data source is successful this test has three phases a range act and assert in the arrange phase as you have seen before we have to stub the data to make sure mock weather remote data source Returns the weather model when we call the get current weather and for this we must use when method and then call the get current weather method in the weather remote data source and finally return the test weather model by using then answer because get current weather Returns the weather model I also Define a variable called test city name and set its value to New York now we can go to the ACT phase in this phase it is enough to call the get current weather method in the weather repository implementation and Store The Returned value in the result and finally in the assert phase using the expected method we check the result is equal to the test weather entity or not because the result is an either so we have to use the either right method to check now we can run the test and see the result as we can see unfortunately the test did not pass but what is the problem this error tells us that we expected the get current weather method to return the weather entity but instead it Returns the weather model if we go to check we will see that the result here is a weather model to fix this error it is enough to create a method called to entity in the weather model and then use this method to convert the result into an entity now I run the test again to see the result as you can see the test was run successfully and there is no problem and we can go to the next scenario the next scenario is to test that get current weather return server failure when the request to API fails the steps of this test are exactly the same as the previous test so I copy it and paste it here and also change its description to this and now it is enough to use then throw instead of then answer in a range and also return server exception instead of returning weather model and finally in the assert phase instead of expecting the result to be equal to weather entity we should expect it to be equal to server failure now it's time to run the test fortunately as we can see this test was successfully passed and now we can go for the last scenario what was the last scenario the last scenario was to get current weather should return connection failure when the device has no internet guys this test is exactly the same as the previous test in order not to waste time I will copy the previous test and paste it here and then change its description to this and now in a Range instead of returning server exception we return socket exception and finally in the assert phase we return connection failure instead of server failure and that's it now we can run all three tests using group as you can see all three tests passed successfully so guys as I said at the beginning of the video in this part we will finish testing the domain and data layers in the next video we are going to work on the presentation layer and test the block so stay with me after working on the domain and data layers the final step is working on the presentation layer at this layer there are State Management and Pages as I said in intro video we will use block for State Management because it is testable and structured so without wasting time let's get started the first thing we need to do is to create a block folder in the presentation so I do this now inside this folder I create a new file called weatherblock test as you know the first step in the test file is to define the main method so I do it in the block we need to call the get current weather method in the repository through the use case and then the repository will get the data from the remote data source and the remote data source through the API and finally return it to us so here we need to access the get current weather use case but we have to mock it so I open the test Helper and add the get current weather use case here and then run this command now I go back to the test and Define an instance of mock get current use case and also we need to Define an instance of weatherblock so I do this but we have an error and the reason for this error is that we haven't created the weather block class yet so let's go do it first in the presentation folder I create a new folder named block as many of you have worked with block and you know here we will have three files named weatherblock weather State and weather event guys this tutorial is not about block teaching so if you don't know anything about block please go and learn it then come here and I can guarantee that the block is very simple and you can learn it easily so let's continue we can start with the event file the first thing we need to do is to Define an abstract class called weather event we will have only one event in this block and we will call this event when the user enters city name so I define a class called on City changed that extends the weather event and also takes a parameter called city name event is finished and we can go to the state file and Define State classes it is exactly like the events first we Define an abstract class called weather state guys we have four states in this block the first state is initial state that I set the class name to weather empty so now I define a class called weather empty which extends weather State class the next state is when we send a request to the API and wait for the API to return the data and in this state we need to show a loading to the user so I Define a state called weather loading and the next state is when we receive the data from the API and can display it to the user so for this I Define a state called weather loaded which has a weather entity parameter in which the weather data is stored and finally the fourth and last state is when we have problems getting data from the API and we have to show the user an error message for this I Define a state named weather load failure and it has a parameter called message which is error text now it's time to create the block class so I open the Weather block file and then define a class called weatherblock that extends block and then set weather event and weather State guys you already know how to define a block so I don't want to waste time explaining it as I said we need to access the get current weather use case so I Define an instance of it that is accepted by the class Constructor also we must specify an initial state by passing it to the superclass Via super which we can pass whether empty block requires us to register event handlers via the on-event API an event handler is responsible for converting any incoming events into zero or more outgoing States so I register on City changed event like this now here first I emit the weather loading State because when the user enters any letter in the text field we call this event and send a request to the API so we have to show loading to user until the data is received from the API then using use case and execute method we get the data and save it in the result here we need to pass the city name entered by the user to the execute method city name is passed to the event so we can pass it to the execute method using the event like this result is an either so by using the fold method in either we can easily determine that if there is a problem in getting data we emit weather load failure State and if it is successful we emit weather loaded state also I want to use Transformer in event handler Transformers dictate how your event will be processed within a block they can help reduce boilerplate code and keep your code organized there are instances when a block event needs to be debounced such as when we're dealing with a search API where each text change triggers an API call so with Transformer we can handle this and create a delay in sending the event so that we don't send a request to the server with every letter that the user enters for this we use the Transformer parameter and then Define a debounce using RX Dart and then set it with a duration of 500 in the Transformer the block is finished and we can use it in test so I return to test file and import weatherblock and then we do the next step which we all know and initialize the instances we defined using setup in the next step I Define an instance of the weather entity because as we have seen in the previous tests we will need it I also Define a variable named city name now we can write first test the first test should be to check that the initial state is equal to weather empty this is a very simple test so we write the test method and set its description and then we check in expected that the weather block state is equal to weather empty and now let's run the test to see the result as you can see test passed successfully and we can go to next test the next test is that block should emit weather loading and weather loaded when data is gotten successfully for Block testing we can use the block test package which makes testing very easy to start we need to use the block test method first we need to set the description for the test this method has a required parameter called build which is exactly like the arrange phase now exactly like the arrange phase we must use when method and call the execute method in the get current weather use case and then return the test weather using then answer and because the return type is either we must use the right method in either and finally we have to return the block for which we write the test this means that we have to return the weather block for the next step which is the act phase we can use the ACT parameter in the test block we must call the event in the ACT phase so we should call the on-city changed event for the Transformer that we set equal to debounce with duration 500 we can use the weight parameter and set its value to duration 500 milliseconds and finally for the assert phase we must use the expect method in the block test in the assert phase we must put the states that we expect for example in this test we expect that first weather loading and then weather loaded will be emitted this test is finished and now we can run the test as you can see the test was passed successfully and now we can move on last test the last test is exactly like this test in the last test we must check that the block should emit weather loading and weather load failure when get data is unsuccessful so I copy the same code and paste it here now first I change the description to this in the arrange phase instead of returning weather we should return server failure and finally in the assert phase instead of emitting weather loaded we should emit weather load failure and finally let's run the test this test also was successfully run and passed the block test is finished it is a very simple test and you can do it easily in the next video we will go to test pages so stay with me I am very happy to see you here and I congratulate you for sticking with me this far I hope you have learned some test writing and write tests for projects from now on because if you want to be a senior programmer you have to do it and always remember this point you can learn by practicing and coding not by watching tutorials I am just teaching you the way and if you want to learn completely you have to practice yourself so let's get started as you know we are now in the presentation layer and we finished block testing and now we have to start the widget testing unlike the previous tests this time I want to start from the actual code and then write the test code for it note that this is against the rules of tdd but I have to do this to teach the widget testing so that it does not confuse you so in the presentation folder I create another folder called pages and then inside this I create a new file called weather page now here first we have to define a class called weather page we can Define State less or state full but for State Management we are using block so we don't need to use stateful always remember this you state less as much as you can in your project before we start creating the home page widget first in the main file I set home property value to weather page then I run the app now in build instead of placeholder I return the scaffold and then I set app bar like this now first I set padding for the body and then I set column in the child in column the first widget we have is a text field in this text field user enters city name and the on-city changed event is called in weatherblock so we must use the unchanged method in the text field and access the text entered by the user and call the on City changed event like this now under the text field I want to display data that block returns for this first I make space from text field using sized box now here we have to use block Builder block Builder handles building the widget in response to new States so we check in the Builder if the state is equal to the weather loading a circular progress indicator will be displayed and if the state is equal to weather loaded this means that the data has been successfully fetched from the server so in the weather loaded State we display weather data guys I will not explain the codes related to UI anymore because it is very simple and surely you know it and if state is equal to weather load failure we will display error message block Builder implementation is finished now if I run the app we will have two errors one related to service locator and the other related to block provider first let's talk about service locator since every class is decoupled from its dependencies by accepting them through the Constructor we somehow have to pass them in we've been doing this all along in tests with the mocked classes now however comes the time to pass in real production classes using a service locator almost every class we created in this course until now has some dependencies even in a small app like the weather app we're building there's quite a lot to configure there are multiple service locators suitable for flutter and as you may remember from the first part we're using get it package get it package is a service locator for Dart and flutter applications it allows you to register and retrieve objects throughout your app making it easy to manage dependencies and decouple different parts of your code let's set everything up in a new file called injection container located directly inside the root lib folder in essence we're going to fill in the constructors with appropriate arguments now here first I Define an instance of get it called locator globally because we will need this everywhere in the project reason why I named it as locator is because it provides a centralized location for managing service locators and dependency injection in a flutter application the next step is to create a method and register the objects inside it so I Define a method called setup locator the setup locator function will be called immediately when the app starts from Main it will be inside that function where all the classes and contracts will be registered and subsequently also injected using the Singleton instance of get it stored inside locator the getit package supports creating Singletons and instance factories since we're not holding any state inside any of the classes we're going to register everything as a Singleton which means that only one instance of a class will be created per the app's lifetime there will be only one exception to this rule the weather block which we're going to register first the registration process is very straightforward just instantiate the class as usual and pass in locator into every Constructor parameter as you can see the getit class has the call method to make for an easier syntax presentation logic holders such as block shouldn't be registered as Singletons they are very close to the UI and if your app has multiple Pages between which you navigate you probably want to do some cleanup like closing streams of a block from the dispose method of a stateful widget having a Singleton for classes with this kind of a disposal would lead to trying to use a presentation logic holder such as block with closed streams instead of creating a new instance with opened streams whenever you try to get an object of that type from get it that's why we use register Factory to register Block in next step I want to register use case we can register rest of the dependencies as Singleton getit gives us two options when it comes to Singletons we can either register Singleton or register lazy Singleton the only difference between them is that a non-lazy Singleton is always registered immediately after the app starts while a lazy Singleton is registered only when it's requested as a dependency for some other class in our case choosing between lazy and regular registration doesn't make a difference since the weather app will have only one page with one block and one dependency tree meaning that even the lazy Singletons will be registered immediately we're going to opt in for register lazy Singleton and now I want to register the weather repository just pay attention guys we should not register the abstract class and instead we should register the implementation class and we should do it like this in next step I want to register weather data source registering data source is exactly the same and we register implementation class notice that they depend on the contract and not on the concrete implementation however we cannot instantiate a contract which is an abstract class instead we have to instantiate the implementation of the repository this is possible by specifying a type parameter on the register lazy Singleton method that's what we did and finally in last step we need to register a HTTP client like this now we have to call this setup locator method in the main method before running the app so that all the classes are registered the implementation of dependency injection is finished as you can see it was very easy contrary to its name the dependency injection problem was solved the next problem was to provide weather block for this we need to use block provider it is used as a dependency injection widget so that a single instance of a block can be provided to multiple widgets within a subtree to provide weatherblock it is enough to wrap the material app inside the block provider I use multi-block provider instead of just block provider because in complex apps we definitely won't have one block and we have to provide several blocks the weather block has to be available to the whole body of the pages scaffold this is where we will get the registered weather block instance from the service locator now it's time to run the app as you can see the app runs without any problems but we need to test to see if all the things we have done are working correctly or not for example I enter New York City and yes as you can see it works well and shows us weather data without any problem now we can move on test writing the first thing we do is to create a new folder called pages in presentation folder and then create a new file inside it called weather page test as you know 100 the first thing we do here is to define the main method now here we need to access weatherblock but not only weather block actually we need to mock it to mock the block we cannot act as before and only Define the class name in the test helper file to mock the block we must use the block test package and the mock block class so here we define a class called mock weatherblock then this class extends mock block now here we have to pass the event and state classes to mock block and finally implement the weather block class that's it now we can define an instance of mock weather block then as we learned we initialize it in setup method here we will test three things the state of the app should change from weather empty to weather loading when filling text field is finished displays a progress indicator when the state of the application is weather loading displays a widget containing weather information when the state of the application is weather loaded so let's write the first test to test the widget we need use test widgets method this method is exactly like the previous method first we have to set the description and then do the three test phases in a Range phase when no word is entered in the text field we display only one text field on the weather page this means that the block state must be equal to weather empty so in the arrange phase we must determine that block state is equal to weather empty this means that weather block State should return whether empty if I want to say very simply because whether page widget is wrapped inside weatherblock that's why we have to set a state for weatherblock in a Range phase which must be equal to whether empty in first test now we can move on the ACT phase in the ACT phase the first thing we always have to do is to build and render the widget we want to test to build and render the widget we use the pump widget method in the widget tester class widget tester is a class that programmatically interacts with widgets and the test environment now here we have to pass the weather page widget but the point that we should pay attention to is that in order for tests to work well we need wrap the widget inside the material app because we have to repeat this work in another test so I create a method above called make testable widget which takes a widget parameter and Returns the material app now please note that if we complete the test and then run it the test will not pass successfully because we use weatherblock in the weather page but we did not provide it in the make testable widget and the block will give us provider error that's why we need wrap the material app inside the block provider and build it with Mock weatherblock and finally now we can use this make testable widget method in the pump widget and pass the weather page to it so let's go on after building and rendering the widget we want to test it's time to test the elements inside that Widget the first thing we had like to do is we need to find the text filed and firm that it exists for this we can use the find by type method is very simple we pass the type of widget that we want to see in the widget it is rendered or not to the find by type method and as we know we want to see if there is a text field or not so I pass the text field now in Next Step using the expected method and then use finds One widget to determine whether or not any widget has the expected text field if I want to say very simply finds One widget claims that we must have exactly one text field in the weather page the next thing we can test is to write text in this text field and then see that text is found in the weather page or not for this we can use the enter text method and first set the text field in which we want to write the text and then the text now to be sure of our test we need to figure out how to inform our widget test to render a new frame after we enter text for this we can use the pump method pump instructs the system to paint a new frame so that we can meet our expectations with a newly updated user interface now we need to find the text that we entered in the weather page widget in expected method for this we can easily use the find text method and set the text that we entered inside it now we can run the test and see the result as we can see the test passed successfully and the text Field Works well so now we can move on the next test the next test is very simple we want to test that the circular progress indicator is displayed when the state is equal to weather loading so I copy this test and then paste it here and first I change the description to this second in when method instead of weather empty I return weather loading and in the ACT phase it is enough to have the pump widget to render the widget that's why I delete the next lines and finally in the assert phase we need to use the find by type method and set circular progress indicator widget we determine that we expect this widget to be displayed on the screen and now I run the test and as we can see this test passed successfully like the previous test so we can move on next test in this test we have to test that whether page should show widget contain weather data when state is weather loaded this test is the same as the previous tests with a little difference so I copy one of the tests and paste it here and then as you know first I change the description to this then we need to change the when method because here we are returning weather loading while we should be returning weather loaded the reason for this error is that weather loaded has a required parameter to which we must pass a weather entity so I Define a weather entity above and then pass it to weather loaded we don't have any changes in the ACT phase so we skip it in the assert phase we should be able to find the widgets which we can show the weather data let's go to the weather page I want to show you something in the weather loaded State we return a column if pay attention we have set a key for this column which is the parent widget in test widgets we can find the desired widget using the key so in expected we can find the desired widget by using find by key and setting the key and finally I run the test to see will the test pass successfully or not as you can see unfortunately test didn't pass successfully the reason is that test widgets by default uses a mocked HTTP class which will always return HTTP error 400. the reason we have this error is because in the real code we are setting an icon using image Network which is an HTTP request to get actual HTTP responses from a web server we just need to use this line code in setup now if I run the test again we will see that the test passes successfully so guys we are done this was our last test of course you can write other tests for weather page but that's enough for this tutorial I am very happy that I could start and finish this tutorial with you I say again please please and please practice and coding and don't expect to learn clean architecture and tdd completely by watching the tutorial because many friends comment that we didn't understand anything or that clean architecture is very difficult that's why I wanted to emphasize again that you have to practice in coding and finally please support me by liking the video and hit the Subscribe button [Music]
Info
Channel: Flutter Guys
Views: 20,585
Rating: undefined out of 5
Keywords: flutter clean architecture, flutter clean code, clean architecture, flutter tutorial, flutter tutorial for beginners, flutter testing, flutter testing automation, flutter tdd clean architecture, flutter testing for beginners, flutter test driven development, testing flutter app, programming, android developer, ios development, ios development tutorial for beginners, kotlin tutorial, flutter bloc tutorial, flutter bloc state management, flutter state management
Id: g2Mup12MccU
Channel Id: undefined
Length: 64min 31sec (3871 seconds)
Published: Mon Sep 04 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.