Build a Clean Architecture Wallet App with Unit Tests - Android Studio Tutorial

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
what's up guys and welcome to this new video about building a clean architecture wallet application first this video won't be about building fancy UI and also the application will be small we don't have any database connection and making Network calls or something like this it's mainly focused on validating our business logic with unit tests and the package structure which we apply and also the different classes will follow the clean architecture principle and we will validate our use cases which represent our business Logic Let me quickly show you what we will build when I click on this plus sign then we can make a deposit and currently the confirm button is disabled but when I enter five for five euro then we can press confirm if I append the dots then the confirm button is disabled again because this is not a valid input and then we can append a 10 for example for 50 and 10 cent then we can press confirm again but if by append another number then this does not make sense for a valid input for a deficit then the confirm button gets disabled again and in this case a use case checks in the background if we have currently a valid input and we will validate this use case to work properly with unit tests when I click on confirm then we have a deposit and also the dates with the time is shown in this UI item we can also make a withdrawal for example 10 euro and now you can see that when we just enter 10 then two zeros get append at the back and we will also validate that this works properly with unit tests and at the top we have the total amount of our valid of our wallet and we will also write unit tests to validate that this use case which calculates the total amount works properly alright okay I'm in an empty chat with compose project in case you use XML for your layouts you should be also good to follow along you only need to build your UI in a different way but the use cases the unit test is also the view model will remain almost the same just a view model we use compose State here you need to use live data then when you use XML for example let's go to our build.grader we have two additional dependencies on the one hand the material icons extended dependency for the minus icon I think this is not in the default icons and we use the Google troop library for our units test assertions and I also added some colors here this colors can be also found in my githubs repository which is linked down below but I think we don't need all of them we only need this orange gray and white and also green and red orange but you can of course also use your own colors let's start with our transactional value input use case which validates if the current amount is actually a valid input let's close this color and build.gradle file then we can go to our root package and create another package called domain this domain package represents the domain layer in terms of clean architecture we always have domain presentation and data layer and this input validation is of course business logic which belongs to The Domain layer in the domain layer we can create another package called use case of course in this small application such a package structure is maybe a little bit Overkill but I will strictly follow the clean architecture principles here when it comes to the package structure in this use case package we can create a kotlin class called pass transactional value input use case let's also refactor this typo this is called transaction of course then we can Define the operator function in bulk it will take a value of type string which is the current input and it will return a Boolean whether this this input is valid or not first we need the decimal point format which we get from decimal format symbols dot get instance dot decimal separator because in my country Germany for example we have a different decimal separator than in the US then we have our first regular expression which kinda describes an input format you can say well reg X regex for regular expression or null because we need to consider null inputs so when the user types in 0 or 0.0 or 0.00 in a little bit different way than the other inputs and this can be described with this string we have 0 plus which means we have have 1 0 or multiple zeros but it's not allowed to have zero zeros then after the user types in one or multiple zeros he can also type in a decimal point format so a DOT or a comma depending on the local and this decimal point format is of type optional because the user does not need to type this in and then after this the user has again the possibility to type in a zero zero times up to two times so with this regular expression we can make something like this 0.0 or also 0.00 and also zero zero zero dot zero and this will cover all these inputs here and if this is the case then we wanna return false because zero and also 0.00 is not a valid input we can't deposit 0 0 or also withdraw zero Euro and we can then say if value dot matches regex and we will pass our regex for now if this is true then we have the case that user puts in zero then we can return false after that we need to take care of our regular expression which describes an actual amount we can call this well regex General is equal to 0 to 9 that means that we have the possibility to type in all values from 0 to 9 and we will append an Asterix which says that this here can happen zero times up to infinity times and after that we will have a decimal point format again of type optional because the user is also allowed to input something like 5 without a decimal separator and then it's here actually inserted a decimal point format we will have again the possibility to insert 0 to 9 but only one up to two times because if the user actually uses a decimal separator then he should be only allowed to put in two additional numbers from zero to nine maybe you're asking yourself is this actually true is this also actually true does this cover all the different valid inputs and also exclude all the invalid inputs I'm not 100 sure either I wrote this regular expression when I prepared this video in about two minutes then I also wrote some test cases they all passed so fine but in production mode I would maybe also add additional test cases and then if you detect an error then you can change this regular expression and you still have your test cases which are fine which actually passed and then you can execute them all again and still validate that this is working correct and also covers your new found error and this is such a cool thing about test cases once they are created and passed they will always remain the same and they help you to be sure that you don't mess things up if you make some changes in your code okay all right after that we can say return value dot matches regex and we will pass our regex general and now it's time to write the test cases for this function for that we can right click on this class and click on generate and then click on test and the testing Library will be a j unit 4. and then we can press ok and this will be generated in the unit test module this is also fine we can click OK and now you can see when we go to our package structure then we have here under tests our same structure then we have in our root package here and it's also recommended to use the same package structure like you have in your production code here we have the root package the domain package and in here the use case package and our transaction value input use case tests and then we can open the curly braces and first we need our use case we can say privateware transactional value input use case is equal to pass transaction value input use case because this is the use case we want to test below that we can Define our first test function we need to annotate this with Edge test and then we can say function test 0 without comma returns false for example so you should always name your test functions so that you can understand that's what's actually tested and then we can say well result is equal to transaction value input use case and we just pass a zero and after that we will say assert that and we don't want to use the junit assert we want to use the assert that from our Google group Library we want to assert that our result is false because 0 is not a valid input and we expect this result to be false and we want to test this if this is actually true then we can click on run and the test case passes so everything works as expected if we for example say is true then we can run it again and then you can see that this test case fails it's expected to be true but this is not the case because our result is false for the next test case we can just copy and paste it down below and this time we want to test zero with comma returns false as well so we will change the input this time 0 comma zero zero because in my case in Germany we use this as a comma separator and not a DOT and then we can also check this test case if it's actually true and this is the case we can also launch all test cases by clicking on this run above on the class and then you can see that both test cases actually passed the next thing I want to validate is if we have multiple zeros before the comma test mile table 0 with comma returns false so we can for example make three zeros before the comma and check on this one and it also passes so everything works perfectly fine with this test case as well and now now we can also check if we have a valid number for example with 0 before the comma we can again copy and paste this you will paste a lot of copy and pasting when it comes to unit tests but also be careful when copy and pasting there are also a lot of potential issues if you don't adjust things right so always be careful there and this test case we want to say test zero before comma and value after comma returns true so this function names are very long sometimes but I think they should be also named the way what we actually testing in this case we want to say 0 comma 20 and this time we want to assert that this is true so that this works because 0 comma 20 is a valid input then we can click on run again and check if everything works and also this test case passes for the next test cases I will just copy and paste some functions you can find them in my github's repository or also try to write your own test cases let's go quickly over them here we will check if this is valid and also if a single number without a comma is valid and a single number which is not zero with a comma value but not two comma values is valid these are all valid inputs and this is again an invalid input because here we have three numbers after the comma discharge uh be false then and we check this and also we will check a value which is isn't actually a value with this letter and the last thing we want to test here if this is actually a valid input and this should also result in true and after that we can click on run and run all test cases and check if they all pass and this is the case and as I already said maybe the regular expression does not work 100 right I'm not quite sure because we only have this test cases they all pass but maybe we don't cover all possibilities let's assume that we find a test case which actually should pass but it does not then we detect an error let's assume that this error is in here and we just change this value to 1 instead of zero and now maybe our detected error is fixed but we still want to be sure that all the logic which worked so far still works let's go to our test cases back and run them again and we can check them okay this test case does not pass anymore this also does not pass anymore and also this does not pass anymore so we maybe fixed our detected error but we changed this code to Zero from zero to one and now all the old logic does or some of the old logic does not work anymore and this makes test cases so powerful I will check change this back to zero and then we can run it again and see that everything passes again but this makes test cases so powerful because you can always rely that once they have passed and you change some code then you can rely on them if you are old logic which works so far still works okay all right I will leave this for now and assume that our use case works properly as I already said maybe you will find the test case which does not pass then you can try to fix it and you still have all your old test cases and can validate that they still pass just by running them let's go back to our logic in our root package domain use case we will have another use case which we also want to test let's create a new kotlin class and this use case will calculate our wallet amount our total wallet amount we have for example five deficits and three withdraws and then this use case make sure to calculate all the different numbers together and will result in a total value we can call this calculate lists some use case and we also Define the operator function here operator function invoke this will take a list of type list loads and it will return a single float then we can say wow result is equal to 0f or LM in list result plus equal LM and after that we can return the result of course this use case is really simple it just sums up all the different array values but maybe in a real world application you also need to calculate from average elements and this array is more complex then you should of course make this a use case and you can also validate its functionality with test cases then for that we go to generate again click on tests J unit 4 again is fine click on OK in the units test module and then you can see that Android Studio takes care to create the right test classes under the right folder there is nothing new here we again have our test functions I will just copy and paste them here and in this case we will have um the assert library or the truth library from Google again let's make sure to import the right one and let's go quickly over them we have our use case which we want to test and first we want to test it for an empty list so an empty list should result in zero and also one element in the list should result of course in this one elements value and here we test the list with multiple values if it gets summed up the right way and also if we have some elements which result in a negative number we also validate that everything works with this test case then we can click on run and check if everything works all for test cases pass I think this is enough here for just sum up some values but maybe you want to apply additional test cases but I think they are enough here to validate this use case functionality one thing I also want to mention is if you test more complex classes for example view models which contains State values and then you execute one test case for example and this state value use get modified or they change then they are not initialized here anymore or in the initialized state and for that you can also apply such a functioner which you can annotate with it before and we will call this function setup and this setup function will always be called before each test case so we could say something like calculate list some use case is equal to calculate list some use case and of course this gets some compiler error because this needs to be a while then but now we make sure that this use case always gets initialized before each test gets executed and for this use case this does not make sense because this use case does not have any properties it only contains its functional and we only invoke this function which does not contain any state but in case you test view models or something like this then make sure you reduce such a setup function to always initialize the view model and provide the initialized state and there's also a function called after which should or in some cases you need to also do some clean up work and this after functioner always gets called after one test case gets actually executed the next functioner we want to Define and also validate with unit tests is an extension function for the float class because later the user can put in 5 for example which is a floating value but we always want to append two zeros then so that we have let me quickly show you this something like this 5 comma 0 0 or also if the user types in something like this then we also want to result in 5 comma 10 and not only 5 comma 1 and for that we make an extension function because later we will work with float values and we can make a float extension functional which we can then easily invoke later you will see what I mean by this for that we will go to our root package create another package called util for me it's more of an utility function because we extends the already provided class float here and but maybe you have another opinion and you want to put this in the domain layer for me it will be the util package and here we will create a new kotlin file called float extensioner we will say function float dot format to price and we will make this function in a real General way so that we can later make a lot of test cases this function will take a look here so we will make this work for different countries or locals and we also pass or Define another parameter always use two command shits because we later can also use a dysfunctional so that it does not append to different zeros for example when we have a single value and this will be of type Boolean and false by default and the functional will return a string first of all we will have a separator is equal to decimal formats Than Words Get instance and here we will pass the Locale and then we get the decimal separator then we have a value multiplied with 10 power of 2 is equal to this multiplied with 100 dot two ins and now we can get the value before the comma and the value after the comma in a real convenient way we will say well value before comma it's equal to Value multiplied with 10 power 2 divided by 100 and where comma values is equal to a value multiplied with 10 power 2 dot absolute value modulo 100. now we can say return if comma values is not equal to null or zero or always use two comma ditches and then we will say where comma values string is equal to if comma values is equal to 0 then we will say 0 0 or else we will use our comma values and then we will say return values before comma separator and comma values string and we will also append a Euro sign in the else case if we have either no comma values or if we don't want to show two comma digits we will simply return value before comma now we can generate our test class again we will click on test again check unit power is still selected click on OK the destination for the test class is also fine and now we can see in our test module we have this util package with our load extensioner tests let's open the curly braces and again I will copy and paste the test cases because I think this is not necessary that I type all of them off let's import tests and also Locale and again we don't want to use this assert class we want to use the one from the droop library from Google so the first test case test float to German format without comma value okay we will pass the local.german to our formats.price function here we don't even needs a value here because we have this float and this is an extension function so we can call it like this we use the local.german and we expect that this here results in 10 euro because if we have a look at the function definition of format to price always use two comma digits is set to false by default so it shouldn't append zeros here um the next test case test float to us formats with out comma values and in this case there's nothing different but we pass a different local so we should of course also check different uh parameter inputs even though the expected result is the same the next test is test float to German format with one comma value so if we put in 10.5 for example we use the local German then it should also append a zero here this is expected because if we have a look at this value here this does not look good we want to and append the zero here of course also the same for us and this time um we have a slight difference here with the dots instead of the comma the next test case is a normal floating value with two comma separated values and here we have again the German um the German version with the comma here everything should work like this we will run this after I went over all the test case here we have test float is zero format to single zero and if we have zero this is also interesting case local does not matter here but we can just use us here and is equal to zero and here we have the first test case with our forcing parameter the test float is zero with two comma values if there is no comma value but it's paused so we have a single zero here but we will force it to append two digits and we have also the local dot us so this is the expected result and here we have also a negative number and we also check on this and here we have a single negative number and again a single negative number down here in the last test case which does not contain any zeros but we will force it to append and the local is us again and in case you want to also check this for Germany then you could could copy and paste this test negative float will enforce blah blah blah and here we will say Germany and just very very simple local dot German and in this case we should have this result let's go up to the class and we can then click on run and check if everything works and here we can see that we have four different test cases which fail and this is uh my badge I immediately see what's wrong here we have an expected value of 10 euro but it is only 10 and this is in the float extension function here because I forgot just forgot to write the Euro sign here and so again a cool example why test cases are so important and here we will go back to our upload extension test and click on run again and then you can see all test cases pass and everything seems to work fine and again I'm not 100 sure if the float extension function actually works the right way in each case if it covers all the possibilities in case you provide your application for alpha or beta testing and the users detects an error then you can try to fix this Arrow go to your floor extension function change this piece of code and provide additional logic maybe to fix the error and then you can go back to your upload extensioner test and you can check if all the previous logic still works okay all right this was it for the business logic which we wanted to test and the rest of the video will be about the composables The View model and setting up the UI and I won't go that detailed over this because this video is more about testing so the explanation won't be two details let's go to our root package and we can also collapse at this test module go to the root package create another package called presentation for the presentation layer in terms of clean architecture and here we will have another package called State and the first state class we want to Define is our transactional UI item so when we later have a deposit or withdraw transaction then we have the representational inside this transactional UI item class here we will have a well description of type string the value of type float well dates of type string and their color of type color then we have another state class called transactional dialog state so when we open the dialog to put in a transactional amount then we will have different states which we need to consider we have on the one hand if the dialog is actually open of type Boolean which is false by default a well type of type transaction type which we need to divide is equal to transactional type dot deposit by default this doesn't matter if this is set to deposit or withdraw by default we can actually also justify the scene in class here we will say enumclass transaction type and here we have deposit and withdraw and then we have an another property wall is confirm Butner enabled this button is only enabled if the input is actually valid which gets checked by our defined business logic use case this is false by default and the current value input of type string which is an empty string by default now we will go to our presentation layer package create another package for our composable components we will call this com components and the first component we want to Define is our transactional dialog this will be a file because it's a composable transactional dialog it will take an on dismiss functioner which does not take and does not return anything the value of the current transaction is of type string provided by a Lambda functioner then we have an on value change functioner which takes a string and returns nothing then at its description either if it's a withdrawal or a deficit transaction of type string and an unconfirmed functioner which does not take and does not return anything and is partner enabled which also gets provided by a Lambda function this Lambda functioner and also this Lambda functional is because it's a little bit more performant if we provide this with Lambda functions instead of for example just a plane Boolean value here the root compostable will be a dialogue and in the undismissed request Lambda we invoke our on dismiss functioner and in here we will have a cards this takes an elevation of 5 DP and the modifier and this modifier will fill the max Smith then we can open this card scope in here we will have a column which takes the modifier this modifier will build a Max width again and we also apply a little padding of 15 DP the horizontal alignment will be alignment Dot Center horizontally the vertical Arrangement will be arrangements.space by NDP then we can open the column scope and first we will have a outlines text field for the value we will invoke our value Lambda function the on value change Lambda functioner takes our on value change and we will provide it and here don't forget the equal sign then we have also a text style which comes from Material theme dot typography dot body one then we will have a label this label will be of types texts and we'll take the description then we have colors which come from text Fields defaults dot text field colors here we have a cursor color which is orange the focused indicator color is orange as well and the focused label color is also Orange the last parameter is the keyboard options which will be keyboard options and Decay boards type will be a Bots type DOT phone so that we have only numbers or can we also use decimal or numbers better I think below our text fields we will have a partner with an on click method and here we will invoke our on confirm past method then we have colors which come from button colors Now button defaults dots button colors the background color will be set to Orange the content color will be whites and this button also takes the modifier this modifier will build a mix with and we also apply a little padding of 10dp let's import this white color as well then this button will have a shape this will be a circle shape and it is enabled or not enabled depending on what this functional is button enabled returns then we can open this button or row scope and here we will have a simple text the text will be confirmed we have font size here of 18 SP it's not very consistent here here we have one size 18 SP and up here we use the body one material theme dot typography but well let's not care about this then we have the font weights font weights dot bolts and the text aligned will be set to text align Dot Center and this was it for the transaction dialog now we can Define our transaction item let's go to components package and create a new file called transactional item this will be the item or the deposit or withdraw item which gets listed below each other when we make actually or when we confirm a transaction app here we will have a composable as well transactional item this will take a color of type color the description of type string value of type floats dates of type string and also a modifier which will be set to a modifier to the default one not the Java one the compose UI one in here we will have a row which takes our past modifier let's import Row for the vertical alignment we will say alignment Dot Center vertically the horizontal align Arrangement will be set to arrangements.space between then we can open the row scope we will have a column this will not take a modifier we will only have a vertical Arrangement which will be set to Arrangement dot Space by 40 5dp and the horizontal alignment will be set to alignment dot starts and it also Imports DP then we can open this column scope here we will have two texts on the one hand the description let's import texts the font weights will be set to font weights dots semi bolts the font size will be set to 18 SP let's import SP then we can copy this text and paste it down below you will have our date and the font weight will be set to normal here and the font size to 13 SP below this column we will have another text for the transaction amount here we will have value dot format to price again our already defined function local dot gets defaults and we will pass true for the always use two comma digits parameter to force two commas if we have a single value with no comma separation then we will have fund rates and Stefan rate will be set to fontbase dot bold the font size will be set to 18 SP and the color will be set to our past color and this was it for the transaction item the last composable component we need to Define is our wallet overview so the rectangle at the top where our current value is actually shown the total value and also the plus and minus sign for invoking or opening the dialog for withdrawal and deficits in our components package we will create a new kotlin file called valid wallets overview and this will be of course also a composable wallet overview and the parameter its tags are mainly Lambda functions we will say value before comma this is again for performance but you can you are also good if you just pass an integer without a Lambda function which returns an integer and this shouldn't matter here then value after comma we pass this uh two integers separately because we want to make the value before the comma bigger and also Bold and the value after the comma will be smaller and not bold then we have an on deposit click functioner which does not take and return anything and on withdraw click function which also does not take and return anything and it will also take a modifier which will be set to the default modifier by default so this allows us to potentially pass modifiers here then we have a role let's import row this row will take our past modifier the vertical alignment will be alignment Dot Center vertically the horizontal Arrangement will be set to Arrangement dot space between so that everything gets pushed to the left and the right then we can open this row scope and first we will have an icon button here this will be the icon button for the withdraw we have the function on with charge click which we invoke here and in the scope of this eigen partner we will Define the icon this will be an image Vector let's import icon first image vector and we can get this from icons dot default dot remove the content description will be a withdraw the tints will be white and the modifier will be set to modifier dot size 32 SP DP and import byte below this eigen button we will have a role with a vertical alignment alignments dot bottom the horizontal Arrangement will be set to arrangement.space by 2dp then we can open the row scope first we will have a text which shows the value before the comma so we will say value before comma and invoke this function the font size will be set to 40 SP the font weights will be set to font weights Dodge bolts and also import SP this will be the single value before the comma then we can copy and paste it down below and in this case we will have um I will just hardcore this comma feel free to reduce the local comma separator but yeah I think I should go quickly over this UI stuff because otherwise the video would be too long and here we will have font size of 24 SP and we won't have a front rates we also need to apply a little bit of a modifier with an offset to make all the different texts with the different text sizes on the same Baseline there's no default parameter to make sure that this happens because they have all the default font padding and well this was just annoying I think but with this little work around we can make sure that they are on the same line and we don't need the traveling reflect modifier the Android compose one and we will say offset for the x value we will say 0dp but for the Y value we will say minus 4 DP like this or not we need to append the step here after the parentheses all right okay then we can copy this text and paste it down below and here we will have our values after the comma so we will say percent 0 2 D dot format and for the parameter we will pass our value after comma this will make sure that we have always two values even if we have only five for example so it will append a zero after the five then the text size the font size Remains the Same also the offset we can copy and paste this again and down here we will only have the Euro sign like this and the last thing we need here is our Plus for the deposit we can copy this icon button and paste it below our row and this time we will have a Content description a deposit and here we will have the add icon and also we need to change the on click method we will say on deficit click and then we have created our valid wallet overview all right okay now we can create our wallet viewmodel and after that we can put everything together and the app should be finished then in our presentation package we will create a new kotlin class called wallet viewmodel this will inhabit from your mother and here we need to Define some State bars the first VAR will be VAR transactional dialog state by mutable state of and here we will provide the default implementation of transactional dialog State Imports the get and set value for the delegates this will be a private set so that we can only modify this VAR from our view model then we have our total amount before comma by immutable state of zero this will be private set as well then we have our total amount after comma by mutable state of zero as well and private set as well then we will also have a well transactional list is equal to mutable State list of and the type will be transactional UI item and here we have a little typo transaction like this and we also need to access our use cases we don't do any dependency injection here normally I would provide these use cases in the Constructor of the view model but since we don't use any dependency injection framework I will just create them here private well pass transactional value input use case is equal to pass transactional value input use case and also privateware calculates list some use case is equal to our calculate list some use case then we have a functional on deposit click here we will say transactional dialog state is equal to transaction dialog state DOT copy this open is equal to true and for the type we will say transaction type dot deficit then we can copy this function and paste it down below and say on withdraw click and change this transaction type to withdraw the next functioner will be our on dismiss dialog function here we simply say it transaction dialog state is equal to transaction dialog state so we initialize it again the next public function will be our on transactional value change which gets invoked each time the user puts in a new character here we get access to the new value and first we need a validational result from our use case we will say pass transaction value input use case and we will pass this new value and then we say transactional dialog state is equal to transaction dialog state DOT copy the current value input is equal to our new value and the is confirm button enabled will be set to our validation result so the confirm button will be only enabled if the validation result is actually true so if we have a valid input then we have the last public functional functional on transactional confirm when the user enters an amount and then it's valid and the confirm button is enabled then you can click this button and this function gets invoked we will have a valid Factor first is equal if transactional dialog States DOT type is equal to transaction type dot withdraw then we have -1 and else we have one because we won't deal with minus at plus signs when the user actually inputs a value we will make this depending on the transaction type then we have a well transactional float value which is equal to a transaction dialogue States dot currentvalueinput dot two float or null right now this transa discriment value input should only contain float values but we will just double check this when we apply this to float or null function and here we will say times vector and if this is actually now we will simply return and do nothing then we will have a well a timestamp is equal to system dot current time Millis also validate is equal to date which gets calculated from the timestamp then we have a well transactional UI item is equal to transaction UI item for the description we will pass depending on the type withdrawal or deficit if transactional dialog States DOT type is equal to transaction type dot withdraw then we will pass withdraw and well let me quickly check if you can actually see this and else we will apply deposit for the description and then go to another line apply a comma here and where maybe this should be in a separate line like this and for the value we will pass our transaction float value the dates will be date Dot tostring and the color again depends on the actual type here we can copy this if statement if it's actually withdraw then we have red orange and else we have green and we can also put this in a separate line after that we will say transaction list dot add our transaction UI item then we will also set the total amount which will be a private functioner which we need to Define and we will say on dismiss dialog now we Define our private function set total amount first we get the total amount from our use case we can say is equal to calculate list sum use case then we are only interested in the transactional lists elements value so we can say map it dot value then we have our total amount here and then we have a value multiplied with 10 power 2 the same value we had when our when we defined our float extensioner here we have our total amount multiplied with 100 and say round to int then we have the total amount before comma which is our state integer is equal to my value multiplied with 10 power 2 divided by 100 and our total amount of the comma which is equal to Value multiplied with 10 power 2 absolute value modulo 100 this absolute value is required because otherwise also the amount after the comma will be set to negative numbers if we have a total negative number all right okay then we can go to our main activity and put everything together let's go to main activity we can also move this inside the presentation layer because we will use the main activity for our main screen normally I would make this a separate composable but I think for the sake of Simplicity this is fine let's put this main activity inside the presentation package click refactor then we also need to change this in the Manifest we need to change it with the package annotation can close the Manifest again first we need access to our view model this will be a private well let's review model is equal to wallet viewmodel and again no dependency injection here we straight initialize it like this in a production application I would use something like dagger Hilt also for providing view models then we can remove this in default stuff also remove the preview and the creating composable first of all we can say window dot status bar caller is equal to Orange dot 2 RGB also window dot navigation bar color is equal to orange.2 RGB for a little bit of um styling then we have a column this will take a modifier and let's import column first the modifier will be compose one fill Max size the background will be set to Gray and we also apply a little padding of 16 DP and then we can open the column scope and imports DP before I forget it we also have a vertical Arrangement here arrangements.spaced by 32 DP horizontal alignment will be set to alignment.center horizontally first we will have our wallet overview and for the different parameters we now need to make use of our view model we will have the value before comma which comes from our wallet viewmodeler dot total amount before comma that's also rearrange this a little bit after that we also have the value after comma which comes from our wallet viewmodel dot total amount after comma then we have the on deficit click functioner here we will say um wallet viewmodeler dots on deposit click and we also have the on withdrawal click here we will say wallet let's go to the right place well like this wallet viewmodel dots on withdraw click then we will also apply a modifier we will say modifier is equal to modifier film x width by 75 percent then we will also have a shadow with 5 DP and rounded Corner shape of 15 DP let me scroll up a bit and then we will also have a background which will be set to Orange and the shape will be also of rounded Corner shape 15 DP and we will have an aspect ratio of 2f so that it's more whites than it's actually high and this was it for our wallet overview and after that we will apply a lazy column for our different transaction list items below the wallet overview we will have a lazy column the vertical Arrangement let's also import this the vertical Arrangement will be set to Arrangement dot Space by 10dp then we can open the scope and we will have items which come from our wallet viewmodel dot transaction list and for each item let's import this items first we will have a transaction item which is the composable we already defined let's import this the color will be set to its dot color the description will be set to to its dot description the value will be set to its dot value the date will be set to its dot date and we also pass a modifier this will fill the mix with the background will be set to whites.copy let's import white first copy 0.8 F and the rounded Corner shade will be 25 DP and we will also have a little padding of 12 DP okay all right the only thing that's left is the dialog which we need to put below this column so down here we can say if wallet viewmodel dot transaction dialog State DOT is open then we want to show the dialog then we can say transactional dialog and for the different functions we again need to utilize our viewmodel for the on dismissed functional we say wallet viewmodel dot on dismiss dialog then we have the value the value comes from our wallet viewmodel dot transaction dialog state DOT current value input well I don't care about the formatting here yeah and for the on value change we will have a valid view model wallet viewmodel dot on transactional value change and we will pass it the description comes from um valid wallet viewmodeler dot transactional dialog States DOT type Dot tostring and we also have the on confirm functioner which will be wallet viewmodel dot on transaction confirm and we also have the on button enabled functional let's make it like this on button is Butner enabled here we will say wallet viewmodel.transaction dialog state that is confirm button enables all right okay then we are good to go to try this out let's launch the application not the tests let's change this to app and then we can launch the application let me put this a little bit more in the middle and the app starts it does not crash so this is good so far then we can click on this plus sign and the confirm partner is also disabled this is fine we need to put in a valid input so 5 would be valid twelve five dots and 12 would be also valid and if we append another value then this confirm button is disabled so everything works fine so far let's click on confirm then also the list item gets displayed the right way let's also do a withdraw for example 10 and then it also appends the two zeros after the 10 so this always this also works fine let's also put in something like this this should cut or this should also work and be passed the right way by null 0.50 this is also fine and uh well let's do another deficit 100.12 and withdrawal for example 91 without zeros and yeah I think this works perfectly fine so far and this was it for this video I hope you all enjoyed it and you could learn something about unit tests and I hope this UI and view model stuff at the end was not too long but I think it's always a little bit more easy to watch if you have a little bit more of a practical example and not only use cases where you apply unit tests in case you want to learn more about building clean architecture applications and also a production application and advanced programming stuff then feel free to subscribe I will do more videos about it and I hope we see us in the next video
Info
Channel: K Apps
Views: 1,911
Rating: undefined out of 5
Keywords: Android, Android State Management, Android Jetpack Compose, Android Compose, Android Multi Module, Jetpack Compose StateManagement, Android Dagger Hilt, Android MVVM, Clean Architecture, Android Best Practices, Android Clean Architecture App, Jetpack Compose Clean Architecture, Android JetpackCompose, Jetpack Compose State Management, Android Clean Architecture, Unit Test, JUnit, Android Testing, Test driven Development, Android JUnit, Android Studio Testing
Id: StdRvEFyivQ
Channel Id: undefined
Length: 58min 32sec (3512 seconds)
Published: Sun Jan 08 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.