MVVM-3: Models should be evolvable/testable independently from the rest of the app.


Description

Well-designed ViewModels should completely decouple Views from Model classes. In such way, by strictly adhering to the MVVM pattern, Models and Views can evolve independently and be tested with ease. Additionally, by applying the inversion of control principle and implementing ViewModels decoupled from the Android framework, it is possible to test ViewModels via unit tests. In contrast, if the binding between the MVVM components is too complex and intertwined, testing and debugging Android apps can become a cumbersome challenge.

Example

We created a simple MVVM example.

This is our current model:

@Entity(tableName = "note_table")
public class Note {
@PrimaryKey(autoGenerate = true)
private int id;
private String title;
private String description;
private int priority;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Note(String title, String description, int priority) {
this.title = title;
this.description = description;
this.priority = priority;
}
public String getTitle() {
return title;
}
public String getDescription() {
return description;
}
public int getPriority() {
return priority;
}
}
view raw Note.java hosted with ❤ by GitHub

We evolve this model, by adding a new feature:

@Entity(tableName = "note_table")
@TypeConverters(DateConverter.class)
public class Note {
@PrimaryKey(autoGenerate = true)
private int id;
private String title;
private String description;
private int priority;
//Newly added
@ColumnInfo(name = "last_update")
private Date lastUpdate;
//Newly added
public Date getLastUpdate() {
return lastUpdate;
}
//Newly added
public void setLastUpdate(Date lUpdate) {
this.lastUpdate = lUpdate;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Note(String title, String description, int priority) {
this.title = title;
this.description = description;
this.priority = priority;
//Newly added
lastUpdate = new Date(System.currentTimeMillis());
}
public String getTitle() {
return title;
}
public String getDescription() {
return description;
}
public int getPriority() {
return priority;
}
}
view raw Note.java hosted with ❤ by GitHub

Since Room cannot convert Date value, we need to write our own converter.

public class DateConverter {
@TypeConverter
public static Date toDate(Long timestamp) {
return timestamp == null ? null : new Date(timestamp);
}
@TypeConverter
public static Long toTimestamp(Date date) {
return date == null ? null : date.getTime();
}
}

The @TypeConverters(DateConverter.class) has been added to the top of the Note model.

We change the version of our database from 1 to 2. And we need to write a migration.

We can now restart the app, and the database has been updated. Maybe this is a bad example, but when we create a new feature for our model. We can evolve our model and test it without creating a new view.

Check this page to view the complete repository.