Solid Principle for app development

SOLID is a mnemonic acronym that helps define the five basic object-oriented design principles:

  • Single Responsibility Principle
  • Open-Closed Principle
  • Liskov Substitution Principle
  • Interface Segregation Principle
  • Dependency Inversion Principle

SOLID #1: The Single Responsibility Principle (SRP)

A class should have one, and only one, reason to change.

for more details, let’s assume we need an adapter of Recyclerview, as you probably already know an adapter takes the data from the data set and adapts it to a view. An implementation I’ve seen could look like this:

// violation of single responsibility principle
public class MovieRecyclerAdapter extends RecyclerView.Adapter<MovieRecyclerAdapter.ViewHolder> {
 
  private List<Movie> movies;
  private int itemLayout;   

    public MovieRecyclerAdapter(List<Movies> movies, int itemLayout)
    {
         this.movies = movies;
         this.itemLayout = itemLayout;
    }
 
    @Override 
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) 
    {
      View v = LayoutInflater.from(parent.getContext())
                             .inflate(itemLayout, parent, false);         
      return new ViewHolder(v);
    }
 
    @Override 
    public void onBindViewHolder(ViewHolder holder, int position) {
     final Movie movie = movies.get(position);  
     holder.itemView.setTag(movie);
     holder.title.setText(movie.getTitle());  
     holder.rating.setText(movie.getRating()); 
     String genreStr = "";  
     for (String str: movie.getGenre()) { 
           genreStr += str + ", ";        
     }     
     genreStr = genreStr.length() > 0 ? 
            genreStr.substring(0, genreStr.length() - 2) : genreStr;  
     holder.genre.setText(genreStr);           
     holder.releaseYear.setText(movie.getYear()); 
     Glide.with(holder.thumbNail.getContext())
          .load(movies.get(position)
          .getThumbnailUrl())
          .into(holder.thumbNail);
    }
 
    @Override
    public int getItemCount() {
        return movies.size();
    }
 
    public static class ViewHolder extends RecyclerView.ViewHolder {
      @Bind(R.id.title) TextView title; 
      @Bind(R.id.rating) TextView rating;        
      @Bind(R.id.genre) TextView genre;
      @Bind(R.id.releaseYear) TextView releaseYear;   
      @Bind(R.id.thumbnail) ImageView thumbNail;
 
      public ViewHolder(View itemView) {
        super(itemView);
        ButterKnife.bind(this, itemView);            
      }
    }
}

The code above violates the Single Responsibility Principle. Coz the adapter’s onBindViewHolder method is not only mapping from an Movie object to the view, but is also performing data formatting as well. This violates the Single Responsibility Principle. The adapter should only be responsible for adapting an order object to its view representation. The onBindViewHolder is performing extra duties that it should not be. An updated onBindViewHolder method could look like this:

// single responsibility principle - Fix it example
public class MovieRecyclerAdapter extends RecyclerView.Adapter<MovieRecyclerAdapter.ViewHolder> {
 
  private List<Movie> movies;
  private int itemLayout;
 
    public MovieRecyclerAdapter(List<Movie> movies, int itemLayout)
    {
         this.movies = movies;
         this.itemLayout = itemLayout;
    }
 
    @Override 
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) 
    {
      View v = LayoutInflater.from(parent.getContext())
                             .inflate(itemLayout, parent, false);       
      return new ViewHolder(v);
    }
 
    @Override 
    public void onBindViewHolder(ViewHolder holder, int position) {
     final Movie movie = movies.get(position);  
     holder.itemView.setTag(movie);
     holder.title.setText(movie.getTitle());  
     holder.rating.setText(movie.getRating()); 
     holder.genre.setText(  
            ArraysUtil.convertArrayListToString(movie.getGenre()));           
     holder.releaseYear.setText(movie.getYear()); 
     Glide.with(holder.thumbNail.getContext())
          .load(movies.get(position)
          .getThumbnailUrl())
          .into(holder.thumbNail);
    }
 
    @Override
    public int getItemCount() {
        return movies.size();
    }
 
    public static class ViewHolder extends RecyclerView.ViewHolder {
      @Bind(R.id.title) TextView title; 
      @Bind(R.id.rating) TextView rating;        
      @Bind(R.id.genre) TextView genre;
      @Bind(R.id.releaseYear) TextView releaseYear;   
      @Bind(R.id.thumbnail) ImageView thumbNail;
 
      public ViewHolder(View itemView) {
        super(itemView);
        ButterKnife.bind(this, itemView);            
      }
    }
}

As Uncle Bob said:

In the context of the Single Responsibility Principle (SRP) we define a responsibility as “a reason for change”. If you can think of more than one motive for changing a class, then that class has more than one responsibility.

SOLID #2: The Open-Closed Principle (OCP)

Software entities (classes, modules, functions, etc…) should be open for extension, but closed for modification

What we are basically talking about here is to design our modules, classes and functions in a way that when a new functionality is needed, we should not modify our existing code but rather write new code that will be used by existing code. let’s dive to it, and show some code:

// violation of Open closed principle
// Rectangle.java
public class Rectangle {
private double length;
private double height;
// getters/setters ...
}// Circle.java
public class Circle {
private double radius;
// getters/setters ...
}// AreaFactory.java
public class AreaFactory {
public double calculateArea(ArrayList<Object>... shapes) {
double area = 0;
for (Object shape : shapes) {
if (shape instanceof Rectangle) {
Rectangle rect = (Rectangle)shape;
area += (rect.getLength() * rect.getHeight());
} else if (shape instanceof Circle) {
Circle circle = (Circle)shape;
area +=
(circle.getRadius() * cirlce.getRadius() * Math.PI);
} else {
throw new RuntimeException("Shape not supported");
}
}
return area;
}
}

As we see above, this code smell like if we have a shape like Triangle or any other polygone, we’re going to be modifying the AreaFactory class over and over. And that’s violate the open closed principle. It is not closed for modification and it is not open to extension. And that’s really bad, so let’s fix that:

// Open closed principle: good example
// Shape.java
public interface Shape {
double getArea();
}
// Rectangle.java
public class Rectangle implements Shape{
private double length;
private double height;
// getters/setters ...
@Override
public double getArea() {
return (length * height);
}
}
// Circle.java
public class Circle implements Shape{
private double radius;
// getters/setters ...
@Override
public double getArea() {
return (radius * radius * Math.PI);
}
}
// AreaFactory.java
public class AreaFactory {
public double calculateArea(ArrayList<Shape>... shapes) {
double area = 0;
for (Shape shape : shapes) {
area += shape.getArea();
}
return area;
}
}

Now, if we need to add a new shape, the AreaFactory will not need to be changed because it is open for extension through the Shape interface.

SOLID #3: The Liskov Substitution Principle (LSP)

Child classes should never break the parent class’ type definitions.

As simple as that, a subclass should override the parent class’ methods in a way that does not break functionality from a client’s point of view. Here is a simple example to demonstrate the concept.

// violation of Liskov's Substitution principle// Car.java
public interface Car
{
public void startEngine();
}// Ferrari.javapublic Ferrari implements Car {
...
@Override
public
double startEngine() {
//logic ...
}
}
// Tesla.javapublic Tesla implements Car{
...
@Override
public
double startEngine() {
if (!IsCharged)
return;
//logic ...

}
}// Make the call
public void letStartEngine(Car car) {
car.startEngine();
}

As you can see in the code above, there are two classes of cars. One fuel car and one electric car. The electric car can only start if it’s charged .The LetStartEngine method will not work if a car is electric and not charged. This breaks the LSP principle since it must be Charged to be able to start as the IsCharged (which also is part of the contract) won’t be set as in the base class.

To solve this you can do something like this:

// Make the call
public void LetStartEngine(Car car) { if (car instanceof Tesla)
((Tesla)car).
TurnOnCar();
car.startEngine();
}

But this violates the Open/Closed Principale, so the proper way is to automatically turn on the car in the StartEngine method like below:

// Fix of Liskov's Substitution principlepublic interface Car {
public void startEngine();
}// Ferrari.javapublic Ferrari implements Car {
...
@Override
public
double startEngine() {
//logic ...
}
}
// Tesla.javapublic Tesla implements Car{
...
@Override
public
double startEngine() {
if (!IsCharged)
TurnOnCar();
//logic ...

}
}// Make the call
public void letStartEngine(Car car) {
car.startEngine();
}

SOLID #4: The Interface Segregation Principle (ISP)

The interface-segregation principle (ISP) states that no client should be forced to depend on methods it does not use.

This principale states that once an interface becomes too fat, it needs to be split into smaller interfaces so that client of the interface will only know about the methods that pertain to them. As you know, the Android View class is the root superclass for all Android views. You name it, if it’s a Button, the root superclass is View. Let’s dive to it, and show some code:

public interface OnClickListener { 
void onClick(View v);
void onLongClick(View v);
void onTouch(View v, MotionEvent event);
}

As you can see, this interface contains three diffrent methods, assuming that we wanna get click from a button:

// Violation of Interface segregation principle
Button valid = (Button)findViewById(R.id.valid);
valid.setOnClickListener(new View.OnClickListener {
public void onClick(View v) {
// TODO: do some stuff...

}

public void onLongClick(View v) {
// we don't need to it
}

public void onTouch(View v, MotionEvent event) {
// we don't need to it }
});

The interface is too fat because it’s forcing to implement all the methods, even if it doesn’t not need them. Let’s trying to fix them using ISP:

// Fix of Interface Segregation principle
public
interface OnClickListener {
void onClick(View v);
}public interface OnLongClickListener {
void onLongClick(View v);
}public interface OnTouchListener {
void onTouch(View v, MotionEvent event);
}

know we can use the interface without implement some messy methods.

SOLID #5: The Dependency Inversion Principle (DIP)

1. High-level modules should not depend on low-level modules. Both should depend on abstractions.
2. Abstractions should not depend upon details. Details should depend upon abstractions.

Let’s start from the code. Many of us probably seen (or written) code like this:

// violation of Dependency's inversion principle// Program.java
class Program {

public void work() {
// ....code
}
}

// Engineer.java

class Engineer{

Program program;

public void setProgram(Program p) {
program = p;
}

public void manage() {
program.work();
}
}

The problem with the above code is that it breaks the Dependency Inversion Principle ; namely item (1.) from above: High-level modules should not depend on low-level modules. Both should depend on abstractions. We have the Engineer class which is a high level class, and the low level class called Program.

Let’s assume the Engineer class is quite complex, containing very complex logic. And now we have to change it in order to introduce the new SuperProgram. Let’s see the disadvantages:

  • we have to change the Engineer class (remember it is a complex one and this will involve time and effort to make the changes).
  • some of the current functionality from the engineer class might be affected.
  • the unit testing should be redone.
// Dependency Inversion Principle - Good example
interface IProgram {
public void work();
}

class Program implements IProgram{
public void work() {
// ....code
}
}

class SuperProgram implements IProgram{
public void work() {
//....code
}
}

class Engineer{
IProgram program;

public void setProgram(IProgram p) {
program = p;
}

public void manage() {
program.work();
}
}

In this new design a new abstraction layer is added through the IProgram Interface. Now the problems from the above code are solved(considering there is no change in the high level logic):

  • Engineer class doesn’t require changes when adding SuperProgram.
  • Minimized risk to affect old functionality present in Engineer class since we don’t change it.
  • No need to redo the unit testing for Engineer class.

https://academy.realm.io/posts/donn-felker-solid-part-5/

Another best example of D

Welcome to the final segment of the SOLID Principles for Android Developer series. We’ve made it to the end, and today I’m going to cover the last letter the SOLID pneumonic acronym, D: The Dependency Inversion Principle (DIP).

If you missed the first four articles, you can easily catch up here:

So without further ado, our fifth and final principle –

The Dependency Inversion Principle states that we as developers should follow two pieces of advice:

a. High-level modules should not depend on low-level modules. Both should depend on abstractions.

and

b. Abstractions should not depend on details. Details should depend on abstractions.

Put succinctly, the Dependency Inversion Principle basically says this:

Depend on Abstractions. Do not depend on concretions.

Migrating to support the Dependency Inversion Principle

In order to fully grok what this principle is dictating I feel that it’s important to talk about how much of software is built – using a traditional layered pattern. We’ll look at this traditional layered architecture and then talk about how we can make changes to it so that we can support the DIP.

In a traditional layered pattern software architecture design, higher level modules depend on lower level modules to do their job. For example, here’s a very common layered architecture that you may have seen (or may even have in your application now):

Android UI → Business Rules → Data Layer

In the diagram above there are three layers. The UI Layer (in this case, the Android UI) – this is where all of our UI widgets, lists, text views, animations and anything Android UI-related lives. Next, there is the business layer. In this layer, common business rules are implemented to support the core application functionality. This is sometimes also known as a “Domain Layer” or “Service Layer.” Finally, there is the Data Layer where all the data for the application resides. The data can be in a database, an API, flat files, etc – it’s just a layer whose sole responsibility is to store and retrieve data.

Get more development news like this Subscribe Comments

Let’s assume that we have an expense tracking application that allows users to track their expenses. Given the traditional model above, when a user creates a new expense we would have three different operations happening.

  • UI Layer: Allows user to enter data.
  • Business Layer: Verifies that entered data matches a set of business rules.
  • Data Layer: Allows for persistent storage of the expense data.

In regards to code, this might look like this:

// In the Android UI layer
findViewById(R.id.save_expense).setOnClickListener(new View.OnClickListener() {
    public void onClick(View v) {
        ExpenseModel expense = //... create the model from the view values
        BusinessLayer bl = new BusinessLayer();
        if (bl.isValid(expense)) {
           // Woo hoo! Save it and Continue to next screen/etc
        } else {
           Toast.makeText(context, "Shucks, couldnt save expense. Erorr: " + bl.getValidationErrorFor(expense), Toast.LENGTH_SHORT).show();
        }
    } 
}); 

In the business layer we might have some code that resembles this pseudo code:

// in the business layer, return an ID of the expense
public int saveExpense(Expense expense) {
    // ... some code to check for validity ... then save
    // ... do some other logic, like check for duplicates/etc
    DataLayer dl = new DataLayer(); 
    return dl.insert(expense); 
}

The problem with the above code is that it breaks the Dependency Inversion Principle – namely item (a) from above: High-level modules should not depend on low-level modules. Both should depend on abstractions. The UI is depending upon a concrete instance of the business layer with this line:

BusinessLayer bl = new BusinessLayer();

This forever ties the Android UI layer to the business layer, and the UI layer won’t be able to do its job without the business layer.

The business layer also violates DIP, because it is depending upon a concrete implementation of the data layer with this line:

DataLayer dl = new DataLayer();

How would one go about breaking this dependency chain? If the higher-level modules should not depend on lower-level modules then how can an app do its job?

We definitely don’t want a simple monolith class that does everything. Remember, we still want to adhere to the first SOLID principle too – the Single Responsibility Principle.

Thankfully we can rely on abstractions to help implement these small seams in the application. These seams are the abstractions that allow us to implement the Dependency Inversion Principle. Changing your application from a traditional layered implementation to a dependency inverted architecture is done through a process known as Ownership Inversion.

Implementing Ownership Inversion

Ownership inversion does not mean to flip this on its head. We definitely don’t want lower-level modules depending on higher-level modules either. We need to invert this relationship completely, from both ends.

How can this be done? With abstractions.

With the Java language, there are a couple ways we can create abstractions, such as abstract classes or interfaces. I prefer to use interfaces because it creates a clean seam between application layers. An interface is simply a contract that informs the consumer of the interface of all the possible operations an implementor may have.

This allows each layer to rely on an interface, which is an abstraction, rather than a concrete implementation (aka: a concretion).

Implementing this is fairly easy in Android Studio. Let’s assume that you have That DataLayer class and it looks like this:

Concrete Data Layer

Since we want to depend an abstraction, we need to extract an interface off of the class. You can do that like this:

Extract Interface

Now you have an interface you can use to depend on! However, it still needs to be utilized because the business layer still depends on the concrete data layer. Going back to the business layer, you can change that code to have the dependency injected through the constructor like this:

public class BusinessLayer {
    
    private IDataLayer dataLayer;

    public BusinessLayer(IDataLayer dataLayer) {
        this.dataLayer = dataLayer;
    }

    // in the business layer, return an ID of the expense
    public int saveExpense(Expense expense) {
        // ... some code to check for validity ... then save
        // ... do some other logic, like check for duplicates/etc
        return dataLayer.insert(expense);
    }
}

The business layer now depends upon an abstraction – the IDataLayer interface. The data layer is now injected via the constructor via what is known as “Constructor Injection”.

In plain English this says “In order to create a BusinessLayer object, it will create an object that implements IDataLayer. It does not care who implements it, it just needs an object that implements that interface.”

So where does this data layer come from? Well, it comes from whoever creates the Business Layer object. In this case, it would be the Android UI. However, we know that our previous example illustrates that the Android UI is tightly coupled to the business layer because it is creating a new instance. We need the business layer to also be an abstraction.

At this point I would perform the same Refactor–>Extract–>Extract Interface steps that I did in the prior example. This would create a IBusinessLayer interface that my Android UI could rely on, like this:

// This could be a fragment too ... 
public class MainActivity extends AppCompatActivity {

    IBusinessLayer businessLayer; 
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}

Finally, our higher-level modules are relying on abstractions (interfaces). Furthermore, our abstractions are not depending on details, they’re also depending upon abstractions.

Remember, the UI layer is depending upon the business layer interface, and the business layer interface is depending on the data layer interface. Abstractions everywhere!

Wiring it together in Android

Herein lies the rub. There’s always an entry point to an application or screen. In Android, that’s typically the Activity or Fragment class (the Application object is not a valid use case here because we may only want our objects to be active during a particular screen session). You’re probably wondering – How do I rely on an abstraction in the Android UI layer if this is the top layer?

Well, there are a couple ways you can solve it in Android using a creational pattern such as the factory or factory method pattern, or a dependency injection framework.

I personally recommend using a dependency injection framework to help you create these objects so you don’t have to manually create them. This will allow you to write code that looks like this:

public class MainActivity extends AppCompatActivity {

    @Inject IBusinessLayer businessLayer;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        // businessLayer is null at this point, can't use it.  
        getInjector().inject(this); // this does the injection
        // businessLayer field is now injected valid and NOT NULL, we can use it
        
        // do something with the business layer ... 
        businessLayer.foo()
    }
}

I personally recommend using Dagger as your dependency injection framework. There are various tutorials and video lessons on how to set up dagger so you can implement dependency injection in your application.

If you don’t use a creational pattern or dependency injection framework you’ll be left writing code that looks like this:

public class MainActivity extends AppCompatActivity {

    IBusinessLayer businessLayer;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        businessLayer = new BusinessLayer(new DataLayer());
        businessLayer.foo()
    }
}

While this may not look too bad at this time, you’ll eventually find that your object graph will grow to be quite large, and instantiating objects this way is very error-prone and breaks many of the SOLID principles. Plus, it makes your application more brittle as changes to the code can wreak havoc on your app. Ultimately, without a creational pattern or dependency injection framework, your Android UI still will not adhere to the DIP.

Patterns for Separating Interfaces

There are two patterns for separating interfaces. Which one you prefer is up to you.

  • Keeping the interfaces close to the classes that implement them.
  • Moving the interfaces to their own package.

The benefit of keeping them close to the classes that implement them is the pure simplicity of it. It’s not complicated, and it’s easy to grok. This has a downside though if you need to do some advanced tooling around your interfaces and implementations or if you need to share these interfaces.

The second method is to pull all of your interface abstractions into their own package and have your implementors reference this package to gain access to the interfaces. The pro of this is that it gives you more flexibility, but along with it comes the con of having another package to maintain and possibly another Java module (if you’ve taken it that far). This also increases the complexity. However, sometimes this is needed due to the circumstances of how the app (and its other related dependencies) is built.

Conclusion

The Dependency Inversion Principle is the very first principle I tend to heavily rely on in every single application I write. In every app I develop I end up using a dependency injection framework, such as Dagger, to help create and manage the object lifecycles. Depending on abstractions allows me to create application code that is decoupled, easier to test, more maintainable and enjoyable to work with (this last one is key to your sanity).

I highly recommend (and I mean it with every ounce of my being) that you take the time to learn a tool like Dagger so that you can apply it in your application. Once you fully grok what a tool like Dagger can do for you (or even a creational pattern), then you can truly grasp the power of the Dependency Inversion Principle.

Once you cross the chasm of dependency injection and dependency inversion you’ll wonder how you were ever able to get by without them. Next Up: More Android Learning Paths

https://blog.shreyaspatil.dev/dont-let-viewmodel-knew-about-framework-level-dependencies

By navalkishorjha

junit test in android from scratch

Android DataBase Test Case

https://commonsware.com/AndExplore/pages/chap-t26-002

Api testing

https://wiremock.org/about/

JUnit Test

https://medium.com/@fabioCollini/android-testing-using-dagger-2-mockito-and-a-custom-junit-rule-c8487ed01b56

https://www.nerd.vision/post/jacoco-coverage-of-util-classes

https://proandroiddev.com/unit-tests-with-dagger2-retrofit2-rxjava2-and-mockk-android-1c268fa77957

https://www.raywenderlich.com/195-android-unit-testing-with-mockito#toc-anchor-001

https://www.raywenderlich.com/books/android-test-driven-development-by-tutorials/v2.0/chapters/5-unit-tests#toc-chapter-010-anchor-002

https://www.raywenderlich.com/books/android-test-driven-development-by-tutorials/v2.0/chapters/6-architecting-for-testing#toc-chapter-011-anchor-002

https://mockk.io/ANDROID.html

Android & Kotlin Tutorials

Android Unit Testing with Mockito

In this Unit Testing with Mockito tutorial for Android, you will learn how to refactor an app in a way that makes it easy to write unit tests in Kotlin using Mockito. By Fernando Sproviero J

Version

  • Kotlin 1.2, Android 4.4, Android Studio 3

The need for writing tests in software development is often underestimated and relegated to later stages of a project. Sometimes not writing any tests is considered right for mobile apps.

You may have heard several excuses, a few of which are:

  • “Mobile apps are frontend apps, the real logic is in the backend, so backend apps should be tested instead.”
  • “Mobile apps are difficult to unit test, because most of the logic is done in the UI. At most, you should only care about UI tests.”
  • “Mobile apps are “simple” or “tiny” compared to backend apps. Thus, effort should be put in the features instead of wasting time making tests.”

Mobile apps are actually becoming larger and more complex. Hence, teams are also growing in size.

Some companies even have separate teams that focus on specific features. And often, there’s another team that has to:

  • Merge all the features.
  • Upload the final build to Playstore/AppStore.

This increase in code complexity and team collaboration may jeopardize the proper functioning of your application.

Writing good tests can cover this!

In this tutorial, you’ll be refactoring a starter project to use the Model-View-Presenter presentation architecture pattern and write unit tests using Mockito.

But first of all, let’s take a look at the testing pyramid.

Testing pyramid

Tests are typically broken into three different kinds:

testing pyramid

The Testing Pyramid, showing the three categories of tests that you should include in your app’s test suite

  • UI Tests:
    These tests interact with the UI of your app, they emulate the user behavior and assert UI results. These are the slowest and most expensive tests you can write because they require a device/emulator to run. On Android, the most commonly used tools for UI testing are Espresso and UI Automator.
  • Integration Tests:
    When you need to check how your code interacts with other parts of the Android framework but without the complexity of the UI. These tests don’t require a device/emulator to run. On Android, the most common tool for integration testing is Roboelectric.
  • Unit Tests:
    The system under test (SUT) is one class and you focus only on it. All dependencies are considered to be working correctly (and ideally have their own unit tests :]), so they are mocked or stubbed.
    These tests are the fastest and least expensive tests you can write because they don’t require a device/emulator to run. On Android, the most commonly used tools for unit testing are JUnit and Mockito.

A typical rule of thumb is to have the following split among the categories:

  • UI Tests: 10%
  • Integration Tests: 20%
  • Unit Tests: 70%

Because unit tests are so important in the testing pyramid and also easy to write, this tutorial will focus on them.

Note: This tutorial assumes you have previous experience with developing for Android in Kotlin. If you are unfamiliar with the language have a look at this tutorial. If you’re beginning with Android, check out some of our Getting Started and other Android tutorials.

Getting started

Download the starter project. Extract the zip file but don’t open the project in Android Studio yet.

You’ll be working with this simple app named “IngrediSearch” that allows users to search for recipes and favorite them.Note: For this app to work, please ensure the following:

  • Get your Food2Fork API key
  • Create a keystore.properties file with the following content (including the quotes):
    FOOD2FORK_API_KEY="YOUR API KEY"
  • Place this file in the root project.

With the keystore.properties file added, open the project in Android Studio 3.0.1 or greater.

Build and run the project to become familiar with the application features.

app welcome pageapp search page
app results pageapp recipe detail page

The project contains the following main files:

  • MainActivity.kt: Contains the main screen.
  • SearchActivity.kt: Allows the user to input ingredients.
  • SearchResultsActivity.kt: Searches for recipes using the API and shows the results. It also provides the ability to add or remove favorites.
  • RecipeActivity.kt: Shows the recipe detail.
  • FavoritesActivity.kt: Shows the list of favorites.
  • RecipeRepository.kt: Interacts with the API to search for recipes. It also stores the favorites in SharedPreferences.
  • RecipeAdapter.kt: Adapter used to show the list in SearchResultsActivity and FavoritesActivity.

Before you start unit testing, it’s best to ask one very important question…

Is this unit testable?

By checking the source code you will find:

Logic in the Activities

class SearchActivity : ChildActivity() {

  override fun onCreate(savedInstanceState: Bundle?) {
    ...
    searchButton.setOnClickListener {
      // 1
      val query = ingredients.text.toString().trim()
      // 2
      if (query.isBlank()) {
        // 3
        Snackbar.make(searchButton, getString(R.string.search_query_required), Snackbar
            .LENGTH_LONG).show()
      } else {
        // 4
        startActivity(searchResultsIntent(query))
      }
    }
  }
}

When the user clicks the search button:

  1. Trim the ingredients text string entered by the user.
  2. Check if it’s blank.
  3. When it’s blank, then show an error message.
  4. If it’s not blank, then proceed to the next screen.

Activity doing everything

class SearchResultsActivity : ChildActivity() {

  private fun search(query: String) {
    // 1
    showLoadingView()
    // 2
    repository.getRecipes(query, object : RecipeRepository.RepositoryCallback<List<Recipe>> {
      override fun onSuccess(recipes: List<Recipe>?) {
        if (recipes != null && recipes.isNotEmpty()) {
          // 3
          showRecipes(recipes)
        } else {
          // 4
          showEmptyRecipes()
        }
      }

      override fun onError() {
        // 5
        showErrorView()
      }
    })
  }

To perform a search:

  1. Because the operation is asynchronous, the activity must show a loading view.
  2. Call the repository of recipes.
  3. If there are recipes, show them!
  4. When no recipes are found, a “No recipes were found” message is shown.
  5. Show an error view if there was an error fetching the recipes.

Adapter doing too much logic

Inside this same activity, you’ll find:

    list.adapter = RecipeAdapter(recipes, object : RecipeAdapter.Listener {
      override fun onAddFavorite(item: Recipe) {
        // 1
        item.isFavorited = true
        // 2
        repository.addFavorite(item)
        // 3
        list.adapter.notifyItemChanged(recipes.indexOf(item))
      }
      ...
    })
  1. Altering the model (favorited status).
  2. Calling the repository.
  3. Updating the view to show the altered model.

Conclusion

Because these are so embedded into the Android activities/adapter, they are not efficiently unit testable. A refactor must be done!

Model-View-Presenter

You’ll refactor the project to the Model-View-Presenter structure. This will ease the creation of unit tests.Note: If you already know about MVP you can skip to the next section where the project is already refactored to MVP and ready to write unit tests.

model view presenter


Also called just MVP

  • Model:
    All your data classes, API connectors, databases.
  • View:
    Activities, Fragments and any Android Views. It’s responsible for showing the data and propagating the user’s UI actions to the corresponding presenter methods.
  • Presenter:
    Knows about the model and the view. Publishes methods that will be called by the view. These methods usually involve fetching data, manipulating it, and deciding what to show in the view.

Refactoring to MVP

Creating your first Presenter

First, you’ll refactor the SearchActivity to MVP.

Therefore, create a new class by right-clicking on the com.raywenderlich.ingredisearch package and choosing New-Kotlin File/Class. Name the class SearchPresenter and add the following:

class SearchPresenter {
  // 1
  private var view: View? = null

  // 2
  fun attachView(view: View) {
    this.view = view
  }

  // 3
  fun detachView() {
    this.view = null
  }

  // 4
  fun search(query: String) {
    // 5
    if (query.trim().isBlank()) {
      view?.showQueryRequiredMessage()
    } else {
      view?.showSearchResults(query)
    }
  }

  // 6
  interface View {
    fun showQueryRequiredMessage()
    fun showSearchResults(query: String)
  }
}
  1. The presenter knows about the view, so it has to hold a reference to it.
  2. When the view is created, it must attach to the presenter.
  3. You must detach from the presenter when the view is destroyed.
  4. This presenter exposes the search method.
  5. If the query is blank then the view has to show a “query required” message. If it’s not blank, it’ll show the results.
  6. A View interface that your activity will have to conform to.

Implementing your first View

Now, open SearchActivity and modify it to the following:

// 1
class SearchActivity : ChildActivity(), SearchPresenter.View {

  private val presenter: SearchPresenter = SearchPresenter()

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_search)

    // 2
    presenter.attachView(this)

    searchButton.setOnClickListener {
      val query = ingredients.text.toString()
      // 3
      presenter.search(query)
    }
  }

  override fun onDestroy() {
    // 4
    presenter.detachView()
    super.onDestroy()
  }

  // 5
  override fun showQueryRequiredMessage() {
    // Hide keyboard
    val view = this.currentFocus
    if (view != null) {
      val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
      imm.hideSoftInputFromWindow(view.windowToken, 0)
    }

    Snackbar.make(searchButton, getString(R.string.search_query_required), Snackbar
        .LENGTH_LONG).show()
  }

  // 6
  override fun showSearchResults(query: String) {
    startActivity(searchResultsIntent(query))
  }
}
  1. Conform to the SearchPresenter.View interface by implementing 5 and 6.
  2. Create a new instance of the presenter and attach the view.
  3. Whenever the user clicks the search button, instead of the activity doing any logic, just call the search method of the presenter.
  4. When the view is destroyed you must tell the presenter to detach the view.
  5. Implement showQueryRequiredMessage required by the SearchPresenter.View interface.
  6. SearchPresenter.View also requires you to implement showSearchResults.

Creating the SearchResults presenter

Create a SearchResultsPresenter class.

// 1
class SearchResultsPresenter(val repository: RecipeRepository) {
  private var view: SearchResultsPresenter.View? = null
  private var recipes: List<Recipe>? = null

  // 2
  fun attachView(view: SearchResultsPresenter.View) {
    this.view = view
  }

  fun detachView() {
    this.view = null
  }

  // 3
  interface View {
    fun showLoading()
    fun showRecipes(recipes: List<Recipe>)
    fun showEmptyRecipes()
    fun showError()
    fun refreshFavoriteStatus(recipeIndex: Int)
  }
}
  1. This presenter will make the API request therefore it has RecipeRepository as dependency.
  2. You also need to attach/detach.
  3. A View interface that your activity will have to conform to.

You may have noticed that you’re repeating the attach/detach logic here, so let’s create a BasePresenter:

abstract class BasePresenter<V> {
  protected var view: V? = null

  fun attachView(view: V) {
    this.view = view
  }

  fun detachView() {
    this.view = null
  }
}

Now extend from this class, remove the view property and the attach/detach methods:

class SearchResultsPresenter(private val repository: RecipeRepository) : 
    BasePresenter<SearchResultsPresenter.View>() {
  private var recipes: List<Recipe>? = null

  interface View {
    fun showLoading()
    fun showRecipes(recipes: List<Recipe>)
    fun showEmptyRecipes()
    fun showError()
    fun refreshFavoriteStatus(recipeIndex: Int)
  }
}

Add the following method:

  // 1
  fun search(query: String) {
    view?.showLoading()
    // 2
    repository.getRecipes(query, object : RecipeRepository.RepositoryCallback<List<Recipe>> {
      // 3
      override fun onSuccess(recipes: List<Recipe>?) {
        this@SearchResultsPresenter.recipes = recipes
        if (recipes != null && recipes.isNotEmpty()) {
          view?.showRecipes(recipes)
        } else {
          view?.showEmptyRecipes()
        }
      }

      // 4
      override fun onError() {
        view?.showError()
      }
    })
  }
  1. This presenter exposes the search method.
  2. Call the repository to get recipes asynchronously.
  3. If the call is successful show the recipes (or empty if there are none).
  4. Whenever there is an error with the call, show the error.

Add the following extra methods:

  // 1
  fun addFavorite(recipe: Recipe) {
    // 2
    recipe.isFavorited = true
    // 3
    repository.addFavorite(recipe)
    // 4
    val recipeIndex = recipes?.indexOf(recipe)
    if (recipeIndex != null) {
      view?.refreshFavoriteStatus(recipeIndex)
    }
  }

  // 5
  fun removeFavorite(recipe: Recipe) {
    repository.removeFavorite(recipe)
    recipe.isFavorited = false
    val recipeIndex = recipes?.indexOf(recipe)
    if (recipeIndex != null) {
      view?.refreshFavoriteStatus(recipeIndex)
    }
  }
  1. Expose the addFavorite method.
  2. Alter the state of the model.
  3. Call the repository to save the favorite.
  4. Tell the view to refresh with the favorited status.
  5. Analogously, expose the removeFavorite method.

Implementing SearchResultsPresenter.View

Now, open SearchResultsActivity and modify it to the following:

// 1
class SearchResultsActivity : ChildActivity(), SearchResultsPresenter.View {

  private val presenter: SearchResultsPresenter by lazy {SearchResultsPresenter(RecipeRepository.getRepository(this))}

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_list)

    val query = intent.getStringExtra(EXTRA_QUERY)
    supportActionBar?.subtitle = query

    // 2
    presenter.attachView(this)
    // 3
    presenter.search(query)
    retry.setOnClickListener { presenter.search(query) }
  }
}
  1. Conform to the SearchResultsPresenter.View interface.
  2. Instantiate the presenter with the repository and attach the view.
  3. Whenever the user enters this screen, instead of the activity doing any logic, just call the search method of the presenter.
  4. When the view is destroyed you must tell the presenter to detach the view.

Implement the SearchResultsPresenter.View interface:

  override fun showEmptyRecipes() {
    loadingContainer.visibility = View.GONE
    errorContainer.visibility = View.GONE
    list.visibility = View.VISIBLE
    noresultsContainer.visibility = View.VISIBLE
  }

  override fun showRecipes(recipes: List<Recipe>) {
    loadingContainer.visibility = View.GONE
    errorContainer.visibility = View.GONE
    list.visibility = View.VISIBLE
    noresultsContainer.visibility = View.GONE

    setupRecipeList(recipes)
  }

  override fun showLoading() {
    loadingContainer.visibility = View.VISIBLE
    errorContainer.visibility = View.GONE
    list.visibility = View.GONE
    noresultsContainer.visibility = View.GONE
  }

  override fun showError() {
    loadingContainer.visibility = View.GONE
    errorContainer.visibility = View.VISIBLE
    list.visibility = View.GONE
    noresultsContainer.visibility = View.GONE
  }

  override fun refreshFavoriteStatus(recipeIndex: Int) {
    list.adapter.notifyItemChanged(recipeIndex)
  }

Implement the missing method:

  private fun setupRecipeList(recipes: List<Recipe>) {
    list.layoutManager = LinearLayoutManager(this)
    list.adapter = RecipeAdapter(recipes, object : RecipeAdapter.Listener {
      override fun onClickItem(recipe: Recipe) {
        startActivity(recipeIntent(recipe.sourceUrl))
      }

      override fun onAddFavorite(recipe: Recipe) {
        // 1
        presenter.addFavorite(recipe)
      }

      override fun onRemoveFavorite(recipe: Recipe) {
        // 2
        presenter.removeFavorite(recipe)
      }
    })
  }
  1. When adding a favorite, now the adapter listener just calls the presenter’s addFavorite method.
  2. Also, when the user wants to remove a favorite, just call the presenter’s removeFavorite method.

MVP Refactor done!

Now that the refactor was done, you’ll be able to create unit tests.

Build and run the app and make sure the app behaves just as it did before the refactor.

You can also download the MVP refactored project if you want to. Remember to add the keystore.properties file in order to open the project.

Using Mockito

Setup Mockito dependencies

Open the application’s build.gradle file and add the following dependency:

dependencies {
  ...
  testImplementation 'com.nhaarman:mockito-kotlin-kt1.1:1.5.0'
  ...
}

Mockito-Kotlin is a wrapper library around Mockito.

It provides top-level functions to allow for a more idiomatic approach and also solves a few issues with using the Mockito java library in Kotlin. Check its wiki for more information.

State & Behavior verification

In state verification, you first have the system under test (SUT) configured with all the necessary dependencies (setup phase). Secondly, you perform a certain operation (exercise phase). Finally, you examine the state of the object and verify it’s the expected one (verification phase). This is sometimes called black-box testing. Usually, JUnit is used for this.

In behavior verification, instead, you specify which methods are to be invoked on the dependencies, in other words, you setup expectations, thus verifying not that the ending state is correct, but that the methods were invoked correctly. This is sometimes called white-box testing. Usually, Mockito is used for this.

Mockito main features

  • Mock:
    The SUT usually has dependencies that are necessary for the SUT to work. If you want to verify a method was called by the SUT, you mock the dependency class and then verify the mocked dependency method was called.
  • Stubbing:
    Sometimes your SUT will follow a certain path or another depending on the results of methods called on its dependencies. To force a dependency to always respond the same way, you need to stub the method.

At last, it’s time for some unit testing! :]

Search unit tests

You’ll create a unit test that verifies that the presenter calls the view’s showQueryRequiredMessage when an empty search is performed.

First delete the ExampleUnitTest:
Delete ExampleUnitTest

Add a new class called SearchTests, with the following content:

package com.raywenderlich.ingredisearch

import com.nhaarman.mockito_kotlin.mock
import com.nhaarman.mockito_kotlin.verify
import org.junit.Test

class SearchTests {
  // 1
  @Test
  fun search_withEmptyQuery_callsShowQueryRequiredMessage() {
    // 2
    val presenter = SearchPresenter()
    // 3
    val view: SearchPresenter.View = mock()
    presenter.attachView(view)

    // 4
    presenter.search("")

    // 5
    verify(view).showQueryRequiredMessage()
  }
}
  1. Every unit test must start with the @Test annotation.
  2. Instantiate the presenter.
  3. Because you don’t need a real view that conforms to the interface, you can mock it.
    Then attach this view to the presenter.
  4. Call the search method with an empty query.
  5. Verify on the mocked view that the presenter calls showQueryRequiredMessage

Now to run the test, press on the little green arrow (the arrow will have a circle after the first run of the tests) to the left of the class declaration:

Run SearchTests

Hopefully you’ll get all green! :]

Successful test results

Refactoring the unit test

Because all SearchTests will basically share the same setup, you should add a setup() function with a @Before annotation as follows:

class SearchTests {

  private lateinit var presenter : SearchPresenter
  private lateinit var view : SearchPresenter.View

  @Before
  fun setup() {
    presenter = SearchPresenter()
    view = mock()
    presenter.attachView(view)
  }
  ...

The function marked @Before is executed before any test, therefore it’s a good place to setup objects/mocks. It’s also common to see a tearDown() function marked @After in test classes, which is run when a test completes.

Modify your first test with the following:

  @Test
  fun search_withEmptyQuery_callsShowQueryRequiredMessage() {
    presenter.search("")

    verify(view).showQueryRequiredMessage()
  }

Verify a method is never called

You can also test that a function is never called by adding the following test:

  @Test
  fun search_withEmptyQuery_doesNotCallsShowSearchResults() {
    presenter.search("")

    verify(view, never()).showSearchResults(anyString())
  }

Note: Android Studio will mark never() as an unresolved reference. Make sure you import the com.nhaarman.mockito_kotlin option which is the Mockito wrapper.

Search results tests

Kotlin default final classes/methods

Before proceeding to create these tests, it’s important to mention that Kotlin classes and methods by default are final. Mockito won’t work with final classes/methods, but there are a few workarounds:

  • Add the open keyword to classes and methods that you’ll mock.
  • Create an interface and have the class implement the interface. Then, just mock the interface (interfaces are open by default).
  • Use mock-maker-inline. You’ll do this later.

Repository refactor

Because in the following tests you’ll mock the repository, we’re going to take the interface approach.

So, use Shift+F6 to rename the RecipeRepository class to RecipeRepositoryImpl and create a new RecipeRepository interface with all the public methods, you can also move the RepositoryCallback outside:

interface RecipeRepository {
  fun addFavorite(item: Recipe)
  fun removeFavorite(item: Recipe)
  fun getFavoriteRecipes(): List<Recipe>
  fun getRecipes(query: String, callback: RepositoryCallback<List<Recipe>>)
}

interface RepositoryCallback<in T> {
  fun onSuccess(t: T?)
  fun onError()
}

Open RecipeRepositoryImpl and implement RecipeRepository:

class RecipeRepositoryImpl(private val sharedPreferences: SharedPreferences) :
    RecipeRepository

Add the override keyword to the corresponding methods (also, be sure to remove RepositoryCallback from RecipeRepositoryImpl if you added it into the interface file).

Also, open SearchResultsPresenter and modify it to use the interface:

class SearchResultsPresenter(val repository: RecipeRepository) :
    BasePresenter<SearchResultsPresenter.View>()

And fix the anonymous RepositoryCallback:

repository.getRecipes(query, object : RepositoryCallback<List<Recipe>> {

Setup Search results tests

Add a new class called SearchResultsTests to the test package:

package com.raywenderlich.ingredisearch

import com.nhaarman.mockito_kotlin.mock
import org.junit.Before

class SearchResultsTests {

  private lateinit var repository: RecipeRepository
  private lateinit var presenter: SearchResultsPresenter
  private lateinit var view: SearchResultsPresenter.View

  @Before
  fun setup() {
    repository = mock()
    view = mock()
    presenter = SearchResultsPresenter(repository)
    presenter.attachView(view)
  }
}

Add the following tests:

  // 1
  @Test
  fun search_callsShowLoading() {
    presenter.search("eggs")

    verify(view).showLoading()
  }

  // 2
  @Test
  fun search_callsGetRecipes() {
    presenter.search("eggs")

    verify(repository).getRecipes(eq("eggs"), any())
  }

These tests verify:

  1. That the view is shown as “loading” whenever the presenter is required to search.
  2. If the repository is called with the corresponding parameters (“eggs”), then the eq matcher is used to verify that it’s called with the same string. The any matcher was used for the callback parameter because you don’t care about it.

Try and run these search results tests using the green arrow next to the test class name.

Stub a callback

Now, create a test that stubs the response of the recipes repository and verifies that the presenter calls showRecipes on the mocked view.

  @Test
  fun search_withRepositoryHavingRecipes_callsShowRecipes() {
    // 1
    val recipe = Recipe("id", "title", "imageUrl", "sourceUrl", false)
    val recipes = listOf<Recipe>(recipe)

    // 2
    doAnswer {
      val callback: RepositoryCallback<List<Recipe>> = it.getArgument(1)
      callback.onSuccess(recipes)
    }.whenever(repository).getRecipes(eq("eggs"), any())

    // 3
    presenter.search("eggs")

    // 4
    verify(view).showRecipes(eq(recipes))
  }
  1. Create a list of recipes.
  2. Stub the repository getRecipes method to return these recipes.
  3. Call the search method.
  4. Verify that the presenter calls showRecipes on the mocked view with the list of recipes.

Go ahead and run the new test to make sure all is good.

Verify state

The following test is just a JUnit test:

  @Test
  fun addFavorite_shouldUpdateRecipeStatus() {
    // 1
    val recipe = Recipe("id", "title", "imageUrl", "sourceUrl", false)

    // 2
    presenter.addFavorite(recipe)

    // 3
    Assert.assertTrue(recipe.isFavorited)
  }
  1. Create a recipe.
  2. Call the addFavorite method.
  3. Verify that the recipe isFavorited flag has changed.

Run the test to make sure the state of a recipe is changed correctly when marked as a favorite.

RecipeRepository tests

Before creating the tests, open RecipeRepositoryImpl and look at the following functions:

  override fun addFavorite(item: Recipe) {
    // 1
    val favorites = getFavoriteRecipes() + item
    saveFavorites(favorites)
  }

  // 2
  private fun saveFavorites(favorites: List<Recipe>) {
    val editor = sharedPreferences.edit()
    editor.putString(FAVORITES_KEY, gson.toJson(favorites))
    editor.apply()
  }
  1. The addFavorite method, first calls getFavoriteRecipes and appends an item.
  2. Then it saves to SharedPreferences using a JSON format.

You’ll create a test that verifies this behavior.

Because you’ll need to stub getFavoriteRecipes() and also you’ll need to call the real addFavorite method, a mock on the RecipeRepository interface won’t work. You need to spy on a real instance of RecipeRepositoryImpl. More on the spy feature later.

Mock maker inline

Now, recall that Kotlin by default has final classes and final methods (unless you use the open keyword).
So, instead of adding the open keyword to the RecipeRepositoryImpl class and methods, create a text file under app/src/test/resources/mockito-extensions called org.mockito.plugins.MockMaker with the following content (it may be easier to switch to the Project view in the Project pane in order to add the new directory and text file):

mock-maker-inline
mockito-extensions file

This will enable you to mock/spy on real classes without adding the open keyword.

Spy the repository implementation

Add a new class to the test package called RepositoryTests with the following content:

package com.raywenderlich.ingredisearch

import android.content.SharedPreferences
import com.nhaarman.mockito_kotlin.mock
import com.nhaarman.mockito_kotlin.spy
import com.nhaarman.mockito_kotlin.whenever
import org.junit.Before

class RepositoryTests {
  private lateinit var spyRepository: RecipeRepository
  private lateinit var sharedPreferences: SharedPreferences
  private lateinit var sharedPreferencesEditor: SharedPreferences.Editor

  @Before
  fun setup() {
    // 1
    sharedPreferences = mock()
    sharedPreferencesEditor = mock()
    whenever(sharedPreferences.edit()).thenReturn(sharedPreferencesEditor)

    // 2
    spyRepository = spy(RecipeRepositoryImpl(sharedPreferences))
  }
}
  1. You need to mock SharedPreferences and the corresponding editor.
  2. Because you’ll stub the RecipeRepository‘s getRecipes method but you also need to call the real addFavorite method on the same object, instead of a mock you need a spy.

Finally, create the test:

  @Test
  fun addFavorite_withEmptyRecipes_savesJsonRecipe() {
    // 1
    doReturn(emptyList<Recipe>()).whenever(spyRepository).getFavoriteRecipes()

    // 2
    val recipe = Recipe("id", "title", "imageUrl", "sourceUrl", false)
    spyRepository.addFavorite(recipe)

    // 3
    inOrder(sharedPreferencesEditor) {
      // 4
      val jsonString = Gson().toJson(listOf(recipe))
      verify(sharedPreferencesEditor).putString(any(), eq(jsonString))
      verify(sharedPreferencesEditor).apply()
    }
  }
  1. Stub the getFavoriteRecipes() with an empty list. Notice that when stubbing spies you need to use doReturn/whenever/method.
  2. Call the real addFavorite method with a recipe.
  3. Check that the subsequent verifications are executed in the exact order.
  4. Verify that the list is saved correctly with JSON format.

Go ahead and run the repository tests to see the spy in action!

Where To Go From Here?

Congratulations! You’ve just learned the basics of using Mockito for unit testing.

You can download the final project here. Remember to add the keystore.properties file in order to open the project.

In this tutorial, you added the unit tests after writing the code. In Test Driven Development (TDD), you write the unit tests first and only add code to pass the currently failing unit test.

I suggest reading the following:

  • Mockito reference: To delve more into this topic, please have a look at the Mockito-Kotlin and Mockito wikis.
  • Mocks aren’t stubs: You’ll commonly hear in the jargon “You should mock that”, but they aren’t always strictly referring to mocks. An article from Martin Fowler explains the difference.
  • Dependency injection: In order to make your app more testable, it’s good to have your dependencies injected somehow. This Dagger 2 tutorial will help you with that.
  • Test patterns: Because writing tests is a bit of an art form, this book from Gerard Meszaros will explain some great patterns to you. It’s an incredible reference.
  • Espresso codelab: If you’re wondering how UI tests are done, this codelab from Google Developers will help you get started.

For even more testing practice, check out our book, Android Test-Driven Development by Tutorials.

By navalkishorjha