Join Chiu-Ki Chan for an in-depth discussion in this video TDD: Red, Green, Refactor, part 1, part of Effective Android Testing for Mobile Developers.
- [Instructor] Test-driven development is a technique to write software that integrates testing into the workflow. It is often described as the red-green refactor cycle. We write a test first before implementing the code. This will lead to a failing test which is the red part of the cycle. You then write the code to make the test pass. This is the green part. Optionally, you clean up the code in the refactor style.
Let's use test-driven development to implement the readFromString function. First, we need to add the junit dependency to build.gradle under the app folder. TestCompile and then junit:junit:4.12. Perform a gradle sync. Go ahead and press okay for this prompt. Go back to Recipe.Java, put your cursor on line five on the class name, and then press command-shift-T to create a new test.
We are going to put it as a jUnit4 test under the test folder. This is because we are writing a JVM test instead of a Android test which will run on a device. In the test resource folder, you will see some recipes. Test, resources, recipes. Let's create a test method to parse water.txt. Go back to RecipeTest.Java.
We will write this method with the annotation Test. This is a part of jUnit annotations to indicate that this is a test method. Public void water. In this test, we are going to parse water.txt and try to create a recipe from it. First, we will use getResourceAsStream to obtain an info stream for recipes/water.txt.
InputStream stream = RecipeTest.class.getResourceAsStream, and then the location of the file, which is /recipes/water.txt. Now that we have a stream, we can create a recipe. Recipe, and then the name is recipe. We are calling the static factory, so Recipe.readFromStream.
Right now, it is not autocompleting, so let's go to recipe.java and see what's happening. Ah, I forgot to make it a static method. So go ahead and add the keyword static to line 16. Go back to recipe test, and now, Recipe.readFromStream and then the stream that we created earlier. What we want is for this recipe to be not null, and then it contains the ID, title, and description that is in the text file.
assertNotNull, and then recipe. Let's run the test. Click on the green triangles on the gutter, and choose run. It fails. Click under the word water on the lower left corner, and then it said that there's an assertion error in line 14. This means that our recipe is actually null, rather than not null, as we want it. This is the red part of red-green refactor.
The reason why it's null is because we didn't do anything in this function. We just asked it to return null. To make it not null, i.e., to make the test pass, we will need to write the code. In readFromStream, create three local variables. String id, we will start it will null. String title, again, initially null. And then also a StringBuilder, which is for the description.
We will call it descBuilder, which stands for description. New StringBuilder. So these are the initial variables, which right now is not useful yet, but we are trying to do the red-green refactor, so don't be tempted to fill in the whole function and parse everything. Instead, if you go back to the test, it's to make line 14 pass. So all we want to do is to make sure that the recipe is not null.
Back to the function. We are going to return a new recipe with the things that we just initialized earlier. Return new Recipe, id, title, and then the description builder dot toString. Let's run the test. Go to the top and click on this green triangle. Yay, passes! We just did our first round of test-driven development, the red-green refactor cycle.
Right now, it's not very exciting. It is just returning a non-null recipe, but still with null values in it. But test-driven development is a great way to enforce that we are actually writing test. Also, we're exercising the code as we write it. Normally, if you're not doing it like this with a small class recipe, you will be writing your whole app, and then deploying to your device, and then iterating through the android app. That is much slower.
If you look at the bottom left corner, you can see that it only takes two milliseconds to run. That is not going to fly if you are trying to do it on Android.
- Why test?
- Local vs. on-device
- Code coverage
- UI testing
- Hermetic environment
- Dependency injection
- Testing with MVP