 Good morning everyone. I am Vidhi Thakrar. I am Nazu. We both are application developers at ThoughtWorks. So, in this session we are going to discuss about a test pyramid of any normal Android application. What are the problems with that test pyramid and together we will achieve the pragmatic test pyramid by applying some design principles. We have created a very small basic shopping application as an example on which we are going to work together to achieve the pragmatic test pyramid. If any of you don't know what this test pyramid then it will be covered in next few slides. Now, Nazni will explain the functionality of this application. Hello. Yeah. Hello. Okay. So, we have created a shopping application Gadget Stop where you can purchase some electronics and accessories. So, when you launch the application you can see some the concise information about some products like the image, the title of the product, the price and as you can see some labels new, popular. So, the business requirement between about this label is if a product is new, the label new will come up. If the product is popular, the label popular will come up and if the product is new and popular, the popular label gets precedence and the popular label will come up. So, as you can see there are no products in my cart right now. Let's go and add some. I click on one of the product. I can scroll down to see the description. Okay. It looks very interesting to me. Let me buy it. I add the item to the cart. If I try to add the item again to the cart, I get a dialogue saying this item is already added to the cart. Again, a business requirement. Now, if I go back, you can see one product is added to my cart. So, that's the functionality we are going to be working with today for the achieving the ideal test pyramid. There is no function. We are not implemented the functionality for cart. Okay. So, let's do a quick code walkthrough for this particular application. So, the very first activity we launch is the shopping activity. In its own create, we set up the action bar. We create the fragment and set up the view pager. Let me go into create fragments. So, there is an electronics fragment and an accessories fragment. Now, each of these fragments inherits from a product based fragment. Let me go into that. So, the product based fragment, basically, on its own create callback, we set some products from the server. So, there's an API call. We make an API call using an API client, a wrapper over the HTTP client. We take in the URL. Okay. Do I need to fetch electronics or do I need to fetch accessories? And I pass a product, I pass a callback. This callback basically, if I get a successful, if I get all my products from the server, I render these products. And if it's an error, I show a dialogue that some, there's some difficulty and close the activity. Let's go to render products. On render products, it's a simple setting up our adapter. It's a base which helps you set the products on the view. We clicked on one of the products. We chose one of the products. And when we clicked on it, we went, we open up the product details activity. Now, when I go to the product detail activity, on its own create, I render the title, I render the description, I render the cost image and the label, either it's popular or new. I do all this on its own create. Well, this is about the rendering part. There's one small functionality left. When I click on the buy button, I add to cut. So either if the product is already in the cut, I get a message, okay, this product is already in the cut. Or if this product is not in the cut, you're adding it for the very first time, you get a message item is successfully added to the cut. So this is the functionality we are going to be working with today. So before we go jump into, okay, what are the various test scenarios and what should be the testing strategies for this product, for this application. I'll ask Vidhi to just explain the importance of test. How many of you have written a test for any example? Oh, nice. What do you think? Why you should write this? Anyone? No one? Yeah, correct. Let me go through quickly the importance of test that why we should write test. It proves that your code actually works. Whenever you develop a new feature for our system, right, we develop it incrementally. So whatever you have developed, it actually works test proof that it improves design. So without thinking of the responsibility of the class and the collaboration between multiple classes, you cannot write test. So it always helps you to design your application correct. It allows refactoring. Just think about a situation that you want to refactor a piece of code and there are no tests around it. It's scary, right? You might miss some scenarios to cover in your refactoring. But if you have tests around that code, then refactoring becomes a pain list because by just running the test, you make sure that you have not broken anything. Good documentation. If you have written a test, you don't need any sort of documentation because test describes the functionality and behavior of your system. It forces you to slow down and think. This is my favorite point. So whenever you write test, you first have to think about the class in which you are going to test something and you also have to think about the expectations from that class in terms of assertions and then only you can jump to the code. It reduces fear and boost confidence. So sometimes test help to catch a very critical situation or a critical scenario of your system. I'll give you an example. For example, there is a legal link in your application, right? Somehow it has been broken. And without testing the application, you have deployed an application. Just think about it. Your company might pay a lot because of such violation. If you write test around it, then you can avoid being in such a situation. It makes deployments faster. If you have written enough number of tests for your system, then just by clicking a test, you can make sure that your system is working as expected and you don't have to spend so much time in manual testing and regression testing and you are ready for the deployment. Is this clear to everyone? Now Nazmin will discuss the test scenarios of the application which we have created. So now that you have got an idea that okay, we feel it's important to write test for this particular application that we saw, gadget stop. What are the tests that you think you know you should write? Anyone? Write test for the labels in the sense new and popular is coming up. Okay. Okay. One. Any other? No duplicate items, right? Okay. So all you're going to assert on the dialogue or the message or what are you planning to test around your for the scenario you just stated. So are you going to be testing some dialogue appeared and this is the message or what are you thinking when you said that? Okay. Anyway, any other scenario one last? Sorry, to check that is no? Error. Okay. Yeah. Okay. Okay. Let's proceed with this. So some of the scenarios that you listed and some of us we had a few scenarios. Now, most of the scenarios six months ago with the team we were working six months ago, if we have thinking of the scenarios, most of the scenarios needed something of the UI to be there. Like we would think, okay, is the new or popular label being rendered on the screen? Is the price being rendered on the same? That is what is going to be seen to the user, right? So you'll think, okay, we need to test it's getting rendered or not. So most of the scenarios we have with the UI for the so we ended up with an inverted test pyramid where we had a lot of tests in the functional side in the sense which needed the UI and a very few unit tests. Now, by unit test, we just checked, okay, the logic in the model, okay, if you know the label is new, popular, something that way we tested in the unit. But most of the tests were on the UI side pertaining to see that, okay, the view is rendering fine. We and this is the inverted test pyramid. Now understand why is this and call an inverted test pyramid or why this is not the great way to go? Let's quickly see what should be the ideal test pyramid, okay? So ideal test pyramid or the pragmatic test pyramid or the testing strategy you can apply for your application should involve three main components, unit, integration and functional. By unit I mean you take one particular individual component and unit tested. These components are tested in isolation. It has a disadvantage, of course, these components are unit tested in isolation. You do not know that, okay, a unit A works in collaboration with unit B perfectly fine. And there is when integration test come into picture. The integration test give you the confidence that, okay, my two, three units are working together. You don't need a functional there and that's where you integration tests shine. Functional in that way helps you as the name suggests gives you confidence that your user flow, what your real user will use is actually being tested is actually working fine. So to just summarize it, integration and unit being technology facing, they help us understand are we building the system right? Just going back to the point where he said are you designing the system right? Are you designing it in a way that it's maintainable, etc. Functional on the other hand puts across are you building the right system? Are you building the system that your clients want or what the users want? So as we move from unit to functional the confidence that you're building the right system increases because you have more coverage on the unit integration and functional of course because you're testing that your system you're building a right system. But at the other hand once you move from unit to functional the speed of your test decrease. The amount of data or the amount of time your functional test run compared to your unit and integration is much more. So let me put this all let me summarize them and put the characteristics of these tests again. So just to summarize unit tests are fast. What I mean by fast is your test refactor cycle is faster. You write a code you test it, it stays you understand okay what is broken you fix it, make some changes again take on your test and you come to something is wrong right? Okay perfect. So it's very quick in feedback. Your feedback is okay something is broken fix it. At the other hand functional you have to build your complete product then run the complete suit on it and then you come to know oh I've broken something it's too late in the feed too late in the cycle to get some feedback that you've broken something. Unit tests they don't have a lot of dependencies first of all you mock most of the dependencies and they do not have UI in the picture so it's much more reliable. Whereas functional you deal with UI and and take into consideration you have a scroll view you may not know that why is your test fail? Has your scroll view genuinely started lagging behind or you know your motion or the emulator is slow and therefore start lagging. You never know why your test has failed it's not really reliable in the in at some time. Unit test as your testing just one single unit it fails you come to know oh this has broken my price is not getting upended with the indian currency that's where it's broken. So when you run the same thing with functional it can be anywhere that failure. It can be that even if your test find a bug that bug can be anywhere in the product. So it does not help you isolate the failure immediately takes you time to understand where exactly something must have been broken. And of course unit don't help you stimulate the real user flow but functional does. So we realize okay we understood these problems we understand the disadvantage of having so many functional tests but still we ended up with such an inverted test pyramid as I showed before. We analyze and figure out okay why do we have less unit test what's the problem. We could not write unit test because there was no capability of dependency injection dependency injection sorry. What I mean is when you're creating so first of all in your view so Android framework gives you activities fragment is where you write all your view logic everything to render talk to the data base talk to the network everything is where you is your starting point. Now as you're creating them in the activity if you can't inject them how will you test it and the reason you can't inject them because you can't create them on your own you can't create a activity you can't create a fragment in the test it's only through callbacks or through intent so if you can't create them you can't inject things and therefore you can't test it first problem I mean the first thing that cause the hindrance in writing unit test okay you all may say come on there was roboelectric why don't you utilize that there are roboelectric test that is for unit testing why don't you write that. Now there are two issues that we face first roboelectric test to write non-non-UI sorry to test non-UI logic you need to set up a lot of data prior to 2.2 version of roboelectric you have to create shadows of activities of fragments etc to test non-UI logic why do I do all this setup when I don't want to test something on the UI but I have to put an entry point to set up all that data and test it second if you're testing get text set text etc you are actually testing them on JVM okay JVM it runs fast it gives me quick feedback but your actual application runs on a DVM so you're running it against an altogether different ecosystem now we didn't really like that and so we ended up writing no unit test JVM unit no J unit unit test no roboelectric unit test we had hardly any unit test so as a team we have come across some we came up to some pattern which will help us increase the unit test we will take us through that okay so as we understood that there are disadvantage of writing so many functional tests let's see what can we do application which we have developed so that we can write unit test easily let's go to the product detail activity here we have returned some rendering logic let me go to the render product popularity here we have quite some logic you can see over here what we are doing here is okay if the product is new then the label should appear as new and the color of that text should be red if the product is popular then label should appear as popular and the color should be purple if there is no product coming if there is no popularity coming in the product then we don't have to display all together this text view and if it is coming then only we want to make it visible so we have quite some logic here can we do something so that we can easily write unit test for this have you guys heard about MVVM how many of you have heard about MVVM how many of you know MVC quite a few I don't have to explain the MVC we can directly move to the MVVM is that fine alright so here the responsibilities and view and model stays the same let me just quickly repeat it what is model anyone sorry so that is like your data right it represents your data which can be retrieved based on the user interaction and it is displayed on the ur what is view you are right which presents to user it's your graphical user interface okay now what is view model here so let me just give you a brief idea about MVVM and then we'll talk about the view model component MVVM was developed by Microsoft Architects to simplify the event-driven programming of user interface cool so what it does is it separates the development of user interface from the development of the business logic which you write now let's talk about what is view model here so the responsibility of view model is it can update the model based on the user interaction and it can also update the view in the MVVM there is one very important component which is not here which is a binder okay what does this binder do so view model exposes the public properties and commands this binder binds the public properties to the view to render it on the UI and it binds the commands which is exposed by the view model which can help to handle the user interaction I will give you a quick example of the binder I don't know how many of you have worked with the Microsoft solution stack but there is a markup language called XAML so XAMLX is a binder which binds this view model on the view alright and it frees developers from being obliged to write boilerplate code to make view model and view in sync are you guys with me till here yeah okay now let's see the relationship between this components here you can see that there is a many to one relationship between view and view model what does this mean this means that as view model does not know anything about the view because that is binder who actually binds the view and view model the same view model can be used by multiple views so that's why there is a many to one relationship let's see the relationship between view model and model it's bi-directional because view model can update the model based on the user interaction and view model is dependent on model because it exposes the public properties which helps to display it on the view so view model is dependent on the model because this is your data cool now this was the code which we were discussing right here we have like quite some conditions over here what do you think can we apply MVVM here yes no yes but we have a very important component of MVVM which is binder and we don't have it so I hope you guys know that android has come up with the binding mechanism recently right it's in beta but they have come up with the binding mechanism where you can bind your data on the view for this application we have not used this binding mechanism so we have to write this boilerplate code to bind the data to the model but so what if we don't have binder for example let's take an example that our activity will act as a binder and let's see that now can we apply a MVVM pattern and can we extract out a view model so that this all logic which we have here can be shifted to the view model yeah let's apply MVVM and see that how this looks like after applying MVVM we have created a view model so we have now this the view you see the before like everything was being done in the view now everything of all the logic has been moved to the view model your view is only asking for the label sex color and the visibility status it doesn't care about okay I have to understand it's new or I have to understand it's popular all that responsibility is delegated to the view model it's just the act view just has to consume whatever the view model gives it so your view has become relatively very thin here now how is this helped us test it right okay so let me go to the view model so your view model takes in a product and it takes some resources it takes in your domain because the domain is giving you the attribute is giving the view model the attribute it needs to massage for the UI so it needs to depend on the domain in this case the product the resources is added here because we need to get certain things from the strings.xml colors or xml so it's past here past in the constructor so once your view model is created let's look at this test we could write because we created this view model so these are j unit test first scenario is the the label the popularity view should be visible as the product is new the popularity view should be visible if the product is popular the popularity view should be visible as the product is new and popular there's a condition right like it's new and popular still it should come up because you have an if condition there your label should be new as the product is new I'm getting a new product I'm getting a product which is new so I should show the new label on the view on the view your label should be popular as the product is popular the main thing is the label should be popular even though your product is new and popular we had a condition right it, popular label should take precedence over new, right. So, that is what we have tested. Similarly, the popularity label color should be red if the product is new, it should be purple if the product is popular and similarly it should be popular, sorry, should be purple if the product is popular and new. So, every single in condition around the popular, around the label is tested. There is no, there is no problem or we do not find any insecurity that something we have missed. Let us quickly see if we tend to make some change in the code, does it really fail? Let me quickly run the test and there it gave me a failure stating I was expecting a popular label, but I got nothing because I removed the code of actually getting a popular label and this was so quick, it ran in 0.3 seconds, I got to know what has failed, where did I make a mistake and I can quickly fix it. So, this has given us advantage of immediately from the unit test of immediately understanding what is my view model going to be 3 and how is it correct, is it not. We have missed one, we missed instance, there is one more thing remaining, we have talked about the data massaging but there is something around the event interaction which we have not talked about, let us look at that. So, after applying this MVVM, let us see that what is the code which is still lying there in the product retail activity. This is the rendering part which we have already covered through view model, let us look at that two cards. Oops, here also we have some business logic, but okay if product is already added to the card then do not add it and display a dialogue message, if the product is not added to the card then save it in the database and then display a toast message. What can we do so that we can unit test this part of the activity also. Have you guys heard about MVP? Yesterday we had a session on MVP, right? I hope you guys know what is MVP. Let me just quickly, okay let us just quickly understand what is MVP, okay? So, here also we have view model, the responsibility of view and model stays the same. What is presenter here, right? So, the presenter is the same thing, it updates the model based on the user interaction and it also updates the viewer, but unlike MVVM we do not have a binder here which means all the blue code to bind the data on the view that we have to write over the presenter. So, MVP was developed mainly to becomes a view very thin and so that view does not know anything about the backend logic which you can write on your application. Everything should be driven by a presenter. So, that's why MVP was are developed. Let's look at the relationship between this components. There is a one-to-one relationship between view and presenter which means that view can hold only one presenter and presenter should only know about one view, right? Because here there is no binder, it's presenter who actually binds the data on the view, right? The relationship between presenter and model stays the same, it's bi-directional. So, let's not talk about that. Now, let's see can we apply MVP on this logic, yeah? The obvious thing which could come to your mind, why are we not using MVVM? View model is also responsible to handle the events, right? Why are we creating something like a presenter? But as we discussed that we don't have binder so that if we want to move this logic to the view model then we have to expose view to the view model which is a violation of the pattern because here you can see that we are updating the UI, we are displaying a dialogue, we are also displaying the toast message. So, if we want to shift this logic to the view model then view model has to know about the view which is a violation of the pattern. So, then we thought let's apply a pattern MVP and let's see that how my after applying an MVP how this code looks like. Wow, it's just one line. Everything has been shifted to the presenter. So, this is my view. You can see here that my view has become really thin now, it's very dumb, it doesn't do anything, right? And everything has been shifted to the presenter. Let's see the code of the product detail presenter. Alright, in the presenter we are passing product as a dependency because this is my model. We are passing a product repository. This is a repository through which I'm saving a product when user adds the product to the cart. Resources as explained by Nazneen if you want to render something and you are depending on the string.xml or colors.xml then you can pass resources and we have a product detail view here. Let's look at the contract of this product detail view. Product detail view can render the title, price, popularity, description. So, this is all my rendering part. Detail view can also display the toast or it can display the dialogue also. So, whatever UI logic which we have to do from the presenter, it is the job of product detail view and my product detail activity is implementing this product detail view. Is this clear? Are you surprised with me? Let's look at the test of this product detail presenter. So, I can write a test for the rendering part which view does. Here, we can write a test that display the toast message on saving the product to the TV. If the product is not inside a TV then save it to the TV and display a toast message. Also, if the product is already added to the cart then don't save it and then display a dialogue message. Product is already added to the cart. We can quickly run this test because it is pure JUnit test. There is no UI dependency over here. So, this test has run like in 0.37 seconds. So, like till six months ago, we were not using the MVP. We just have MVVM in our project and we were just writing unit test for the data massaging part. But then we spend some time to analyze that what can we do with the user interaction part and then we came up with this. So, we have applied two patterns here. MVVM and MVP. Let's look at the bigger picture that after applying this two pattern, how my application architecture looks like. So, here we have view, we have view model. Also, we have model. So, the relationship between this is same as whatever was there in the MVVM. We have view, presenter and model. Also, the relationship between this component stays the same as it was in MVP. The only addition here is this. So, what does this mean? So, actually whatever, so view is very dumb. So, view delegates everything to the presenter and it is the presenter's responsibility to create a view model and update the view based on the view model. So, here now a presenter acts like a binder and presenter is also responsible to handle the user interaction. Is this clear? Yeah, cool. Any questions so far? No questions? Yeah, go ahead. So, here, so what view model, anyways view model does not know anything about the view, but view knows about the view model, right? View is depending on the view model because based on the data whatever we have in the view model, we are rendering it on the view. So, actually there is no dependency of the view on the view model and that was there in MVVM also. In MVVM also there is no dependency of view on the view model. We aren't invoking anything of the view like show to show dialogue of the view from view model. It's only pure massaging of the data. Let's move forward. One more question if you want and then because these are on the pattern then we'll move to the next level of test. Yes, we have we will share the code which is this fine. Yeah, we will share the code which is all of it. It's on GitHub, we push push push in the input in the url so you can take it from there. Yeah, let's let's let's see that what can we do with the integration testing here? Right, so kind of using a mix of the MVVM and MVP, we achieved a good number of unit tests at our end like we could increase the number of unit tests in our in our code base. So, it helped us also scale our team. So, otherwise if the team was growing and we had no confidence of what we are building, we kept stepping up stepping on each other's work each other's features and it and things kept breaking and we came to came to know about it very late. So, this helped us here. This this is this is evolving this architecture as as as we go on working on our project. So, this is about unit when we go to integration, we have so we have tested presenter in isolation. We have tested the view model in isolation, but we haven't checked the binding of these components. So, from the view, the view talks to the presenter, presenter talks to the view model. This kind of binding the collaboration between these components are not tested, right? We need some confidence that this is working fine. So, let me let me walk you through the scenarios for the integration test. First, let me change the build variant. We have tested, okay, the UI, the image, the title, the label, that the cost is coming up on the UI or not. So, we have used Expresso here which helps us test that okay on these particular IDs is this particular ID actually the value is being seen or not. So, we have tested that that everything is rendering fine on the UI. Second one is, okay, are you being able to scroll? So, we have a scroll view on our details activity, right? Are you being able to scroll and then scroll to the bottom and then click on buy and is the product actually getting added to the DB? That's the second test. So, we have also tested interaction with our database. Third scenario is the dialogue of, okay, the item, sorry, is the dialogue of the item already added in the cut actually coming up on the screen. That's the third test. So, let me just run these tests quickly. Need a functional test to completely build the APK, then run the synchronous nothing. So, we could get a good understanding, okay, all my components I tested in isolation are working fine and I'm getting whatever is needed on the screen. So, this is with integration test. Now, there's only one functional test and I'll show you on. We ended up with only one function because everything else was tested and what the function test was, sorry, what the function test is. It's just about, okay, I launched the very first activity, okay, I'll actually open it up. I launched the very first activity, shopping activity. I scroll, I scroll to the data, I can click on the electronic, I click on the item in the electronic tab, then it opens the product detail activity, I click on cart, I purchase it, I press back and I can see that the number of items in the cart has increased. This is what a real user will do, right? This is what your app was intending to help the user do. Go see the details, purchase it and see that my item is in the cart. We ended up with, let me show you the coverage. We actually ended up with 20 unit test, six integration and only one functional. All together the other way, then we were some six months ago with the inverted test pyramid. So, we have, so we kind of achieved the pragmatic test pyramid. So, this is Jacoco code, the Jacoco result, code coverage result. So, this is around for the view model. I have 100% coverage for my product view model. Every single statement is unit tested there and for the presenter, which allows you to save the product, render the product, I have 100% coverage. So, this also gives me confidence everything is tested fine. At the end of it, applying MVVM, MVP, writing of your integration and functional test, we kind of came up to the correct test pyramid, which is suggested, a 70% at unit, 20% at integration and 10% at functional. That is how the ideal, how Google suggests and we, we achieved it in the last six months. So, point we learned and you want to share with you all, unit testing or TDD or you know, the achieving the correct test pyramid is not only for web, it's very much possible for mobile application and mobile application should not be related. Okay, it's a different set of tools, it's a different, it's a different ballgame altogether. So, it's not achievable. It's very much achievable and it has helped us also from the capability of our team also get a faster feedback all the advantages I just discussed. So, yes, that's it from us. Thank you and any questions, please open for questions. Okay, you can go ahead. I'll answer the first one around how do we understand what do we have to, where do we have to test. So, anything that, anything that deals with massaging of the data, like, okay, again, going back to my example or take a book, my show also, if you have to show some Indian currency or some data, which is particularly only for the view. It may have to be later with, let it, let us keep aside from where we got the data, we got the data and we need to render it for the view that we put in a view model. So, anything that is related to massaging, regardless of where the data is coming from, we put it in view model. Coming to where we understand, okay, we need a presenter here, whenever we need to talk back to the view, like kind of, we need to show a toast or we need to show, okay, our dialogue is a loader. A loader has to be started or you need to start some other flow. You need to talk to the view that is when you put it in the presenter. So, that is how we decide the interactions. There is, I don't know, I hope that helps to answer that. I would have to interrupt. I'm so sorry. We are running out of time. We can definitely take the Q and A outside. Let me, we can talk offline. Thank you. Thank you.