Uploaded by omardope1

Android-Architecture

advertisement
Android
Architecture
About This Ebook
We are sharing with you the process behind the most of our fantastic Android apps!
After a series of technical blog posts, we put together this exclusive White Paper. Learn
about using Android Architecture for building amazing apps in this 47 page E-book,
covering everything from the basics, introducing and applying Clean Architecture on
Android, to testing.
About Five
Five is a mobile design and development agency founded in Croatia with strong presence in New York. We help brands like Rosetta Stone, Rhapsody, Marriott, and many
others, to build and execute their mobile strategy and products. Five and our product
company, We employ over 130 designers and developers in Croatia and the US.
Table of contents
Part 1: Every new beginning is hard
Pain points of the early Android years: does Google care at all?
Four golden rules of Android architecture (or any architecture really)
I. Satisfy a multitude of stakeholders.
II. Encourage separation of concerns.
III. Run away from the real world (Android, DB, Internet…).
IV. Enable your components to be testable.
First iteration – God activity
Second iteration – MVP
Third iteration – MVP + managers
Conclusion
2
3
4
5
6
6
6
7
8
9
10
Part 2: The clean architecture
Master of your domain
I. Dependency rule
II. Abstraction
III. Communication between layers
Conclusion
11
12
12
13
15
18
Part 3: Applying clean architecture on Android
Entities
Use cases
Repositories
Presenters
Device
DB & API
UI
Modules
Is it better?
It may be complicated – but it’s worth it
19
20
20
20
21
21
22
22
22
23
25
Part 4: Applying Clean Architecture on Android, Hands on (source code included)
Foundations
I. Domain
II. Data
III. Device
IV. The easiest part, app module (UI module)
Dependencies
Bricks, a lot of bricks
Domain
UI
Let’s throw some rx in the mix
Data and Device
Models, models everywhere.
Conclusion
26
26
27
27
27
27
27
29
29
32
34
35
36
36
Part 5: How to Test Clean Architecture
I. Testing clean architecture
II. Naming tests
III. Testing domain
IV. Testing the data
V. Testing the app module
Conclusion
37
38
38
40
43
44
47
PART 1
Every new
beginning is hard
Tomislav Homan
The goal of this post series is to give an overview of our struggles with the architecture
PART 1: EVERY NEW BEGINNING IS HARD
of Android apps. I realized that however painful the implementation of android app
architecture might be, it turned out to be the fundament of every fantastic app I have
been working on.
Every technology has its natural evolution. Or to be more precise, its community undergoes the process of evolution. Early adopters of a new computer language or a framework are enthusiasts who just want to get their hands on the technology and get some
stuff done as quickly as possible. Typically, the new community is small and has limited
potential for knowledge transfer among developers, that is, everybody is learning from
their own mistakes since there are no architecture guidelines available yet.
Android in its early days wasn’t an exception. It was cool and attractive for developers,
and it offered an opportunity for the early adopters to be a part of community around
2
this fast-developing technology.
Pain points of the early Android years: does Google care at all?
You can argue that there are numerous senior guys out there who have a lot of experience with different technologies and who will come up with standards in no time. Well,
not necessarily. If there is a strong company behind the technology that counts on making money, sure, they’ll have their own technology evangelists who will spread the word
about how this new language is cool and you can do a lot of stuff with it and it’s easy
peasy to learn, scale, and satisfy millions of customers.
Microsoft does that often with its technologies. On the other hand, when Google bought
Android I honestly think they treated it just as some other side project. If you have been
in the Android world since its beginnings, you must remember the frustrations and the
feelings that Google simply doesn’t care about you. Those couple of guys who have extra experience, ability, and the will to help the community are now Android superstars or
little demi-gods—Jake Wharton for example.
“When Google bought Android, I honestly think they treated it just as
some other side project.”
Another thing you could say is that you don’t have to think a lot about the architecture
and the organization of your code because the framework does that for you. Android
forces you to put your screen stuff into activities, reusable screen stuff into fragments,
and background stuff into services and to talk to others using broadcast receivers, so it
pretty much makes your life good. Right? No.
First of all, there are some good practices and principles that are simply good, regardless
PART 1: EVERY NEW BEGINNING IS HARD
of technology. The single responsibility principle for example, or the dependency inversion principle, program against interfaces, kill global state, try to murder all states, etc.
Frameworks rarely force you to follow principles. Au contraire, they violate them in the
worst possible ways. Think of context, the God object that you have to use everywhere,
all kinds of singleton managers, fragments with their lifecycles (what kind of nightmarish
life is that), AsyncTasks that are usually implemented incorrectly, so they suck the memory, food, water, and air out of your app.
It’s easy for a young developer without guidance to make a monster instead of an app.
Think about it as a spaghetti monster black hole—it’s good as a pastafarian deity, but
not as good an app as it could be.
3
And lastly, technology and frameworks hide the purpose of the app. Ok, it’s an Android
app, but what kind of an Android app? Newsreader? Music player? Language-learning
app? Weather app? Maybe it is a yet another to-do list app. If everything is bundled up
in the classes provided by the framework, you cannot tell.
As Robert Martin, aka Uncle Bob, says, “Your architecture should scream the purpose of
the app.” More technically said, business logic should be clearly separated and independent of the framework.
Four golden rules of Android architecture
(or any architecture really)
PART 1: EVERY NEW BEGINNING IS HARD
4
I hope that it’s clear now that you cannot rely on the framework to make your code
neat and organized, especially with Android. At Five, we realized that a long time ago
but lacked the experience to come up with a bull’s-eye right away. It takes a lot of time
for fails to manifest, and you cannot change the whole architecture in the middle of the
project. You also can’t get time to refactor the old project into the new, cool, “wanna
be” best architecture. So, we took the gradual approach, slowly building up our architecture from project to project and learning from our mistakes. There are couple of
goals that we think our architecture should satisfy, and we can compare our approaches
against those goals. Good architecture should do the following:
1.
Satisfy a multitude of stakeholders.
2.
Encourage separation of concerns.
3.
Run away from the real world (Android, DB, Internet…).
4.
Enable your components to be testable.
I. Satisfy a multitude of stakeholders.
Stakeholder (in this post) is any person interested in the development of your app. Besides developers, there are UI designers, UX designers, PMs, DB admins, QA, etc.
And of course, you can’t organize your code in a way that a UX designer, for example,
could open a project and understand everything immediately and maybe even make
some changes. It’s a unicorn.
PART 1: EVERY NEW BEGINNING IS HARD
5
What I mean is that you CAN organize your code in a way that a developer who currently works WITH the UX designer can manage only the stuff related to the UX. So, all
interactions are separated in the classes/modules/components/whatever whose job is
interactions, and that particular developer works only on those components while working on the UX part of the app.
II. Encourage separation of concerns.
What I have said before is an example of separation of concerns. We encourage that
particular approach because it maps nicely to the organization of teams and project
phases, but your architecture should also encourage separation of concerns generally.
Separation of concerns, or the single responsibility principle, says that every component
should have only one reason to change.
III. Run away from the real world (Android, DB, Internet…).
This particular point, to run away from the real world, was already mentioned before. We
have already said that we want to scream what the app really does; that’s it. We want to
emphasize business logic and leave framework details under the hood. This point should
be even stronger: we would like to not only hide framework details but all the details
related to the outside world.
PART 1: EVERY NEW BEGINNING IS HARD
All the nitty gritty dirty Android stuff like sensors, notification mechanisms, screen details, database access, Internet access, etc.
IV. Enable your components to be testable.
You should unit test your app as much as possible and your architecture should allow
6
you to do it. If you can’t unit test everything, you should at least cover your business
logic with tests. Separation from the real world goes nicely with this. It’s easier to test
your business logic if it is clearly separated from the rest of the app.
First iteration – God activity
public final class UsersActivity extends ListActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//...
new ListUsers().execute();
}
private final class ListUsers extends AsyncTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void... voids) {
// final SQLiteOpenHelper sqLiteOpenHelper = ...
// JsonObjectRequest jsObjRequest = new JsonObjectRequest
// (Request.Method.GET, url, null, new Response.Listener<JSONObject>() {
// MySingleton.getInstance(this).addToRequestQueue(jsObjRequest);
// showData(user);
return null;
}
}
}
You have probably seen code like this back in the “olden times.” If you haven’t, you’re
young. What’s wrong with this? Everything!
PART 1: EVERY NEW BEGINNING IS HARD
7
We have an activity that touches databases, goes to the Internet, does parsing, spawns
threads, and renders data. So, all the stakeholders are looking at the single class, none
of the concerns is separated, it’s not testable, and business logic is mixed with the
Android stuff.
Second iteration – MVP
The first approach obviously didn’t work. One of the first things we tried was MVP, or
model-view-presenter. Everybody is familiar with MVP. It is one of the most popular
architectural patterns. It looks something like this:
Here we have separated views that are actually our Android fragments, we have (domain) models that represent our business, and finally we have presenters that coordinate all of that. It’s better for sure. Concerns are somewhat separated, stakeholders aren’t so confused anymore, and you can write some tests. We are still mixed up with the
real world, though, as the presenter directly touches the database and everything. The
elephant in the room here is the presenter. It’s a god object. It deals with the models, it
sends the data to the view, it holds the business logic (business logic are those gears :)
), and it goes to the database and Internet, fetches sensor data, etc. So yeah, better, but
PART 1: EVERY NEW BEGINNING IS HARD
8
it could get way more better.
Third iteration – MVP + managers
What does government do when it doesn’t know what to do? It forms an agency. What
do developers do when they don’t know what to do? They introduce some manager. You
don’t have to name it “manager.” There are lots of names for these kind of classes: utils,
helpers, fooBarBuzz-ator, etc. So, we introduced managers.
To be honest, this even sort of worked. Business logic is contained in the manager classes. Stakeholders know where to look at, concerns are sort of separated but they could
be even more so, you can write more tests but you are still touching Android directly so
you have to write Android test cases and prefill the database to test business logic and
that’s slow. And yeah, managers tend to be huge beasts and very quickly become hard
to maintain. You can argue that it doesn’t need to get more complicated than it is, that
you can deliver code faster by using simpler architecture, but there will be more bugs
with that approach, and maintainability will suffer.
PART 1: EVERY NEW BEGINNING IS HARD
9
Conclusion
In this first part of the series, we went through the challenges of creating an Android
architecture that actually works. Good Android architecture should satisfy a multitude
of stakeholders, encourage separation of concerns, emphasize business logic and leave
framework details under the hood and enable all your components to be testable. In the
second part of the series, we will show you how we managed that what worked for us.
Until then, do you have any suggestions on how to create a proper Android workflow?
What were the problems you encountered?
PART 1: EVERY NEW BEGINNING IS HARD
10
PART 2
The clean architecture
Tomislav Homan
In the first part of the series, we covered the mistakes we had made on our path to
finding the architecture that works. In this part, we will present the so-called Clean
Architecture.
The first image you come across when you google “clean architecture” is this:
PART 2: THE CLEAN ARCHITECTURE
11
It is also known as onion architecture, because the diagram looks like an onion (and it
makes you cry when you realize how much boilerplate you have to write); or ports and
adapters, because there are, as you can see, some ports in the bottom right corner. Hexagonal architecture is yet another similar architecture.
Clean architecture is the brainchild of previously-mentioned Uncle Bob, who also wrote
books on Clean Code and Clean Coder. The main point of this approach is that the business logic, also known as domain, is at the center of the universe.
Master of your domain
When you open your project, you should already know what this app is all about, regardless of the technology. Everything else is an implementation detail. For example,
persistence—it’s a detail. Define an interface, make in-memory a quick and dirty implementation, and don’t think about it until the business is done. Then you can decide how
you really want to persist the data. Database, internet, combination, file system—maybe
leave them in-memory, maybe it turns out you don’t have to persist them at all. In a sentence: inner layers contain business logic, outer layers contain implementation details.
That being said, there are a couple of features of clean architecture that enable that:
1.
Dependency rule
2.
Abstraction
3.
Communication between layers
I. Dependency rule
Dependency rule can be explained by the following diagram:
PART 2: THE CLEAN ARCHITECTURE
12
Outer layers should depend on inner layers. Those three arrows in the red square represent dependencies. Instead of “depends on,” maybe it’s better to use terms like “sees,”
“knows about,” or “is aware of.” In these terms, outer layers see, know about, and are
aware of inner layers, but inner layers neither see nor know about, nor are aware of,
outer layers. As we said previously, inner layers contain business logic and outer layers
contain implementation details. Combined with the dependency rule, it follows that
business logic neither sees, nor knows, nor is aware of, implementation details. And
that’s exactly what we are trying to accomplish.
How you implement the dependency rule is up to you. You can put it in different packages, but be careful not to use “outer” packages from “inner” packages. However, if
somebody is not aware of the dependency principle, nothing would stop them from
breaking it. A better approach would be to separate the layers into different Android
modules, for example, and adjust dependencies in the build file so that the inner layer
simply cannot use the outer layer. At Five, we use something in between.
One more thing worth mentioning is that, although nobody can stop you from skipping
layers—for example, using some red layer component from the blue layer component—I
strongly encourage you to access only components from the layer next to yours.
II. Abstraction
Abstraction principle has already been hinted at before. It says that, as you are moving
towards the middle of the diagram, stuff becomes more abstract. That makes sense: as
we said that the inner circle contains business logic and the outer circle contains implementation details.
PART 2: THE CLEAN ARCHITECTURE
13
You can even have the same logical component divided between multiple layers, as
shown in the diagram. The more abstract part can be defined in the inner layer, and the
more concrete part in the outer layer.
An example will make it clear. We can define an abstract interface as “Notifications” and
put it in the inner layer, that way your business logic can use it to show the notification
to the user when it wants to. On the other hand, we can implement that interface in such
a way that the implementation uses Android notification manager to show the notifications, and then put that implementation in the outer layer.
In this way, business logic can use a feature—notifications in our example—but it doesn’t
know anything about implementation details: how the actual notifications are implemented. Moreover, business logic doesn’t even know that implementation details exist.
Look at the following image:
PART 2: THE CLEAN ARCHITECTURE
14
When you combine abstraction with the dependency rule, it turns out that our abstract
business logic using notifications neither sees nor knows about, nor is aware of, concrete
implementation that uses the Android notification manager. That is good, because we
can switch that concrete implementation and business logic won’t even notice it.
Let’s just briefly compare this to how abstraction and dependencies look and work when
using standard three-tier architecture.
You can see in the diagram that all dependencies in the standard three-tier architecture
go to the database. That means that abstraction and dependency don’t match. Logically, the business layer should be the center of the app, but it isn’t, as dependencies go
towards the database.
The business layer knows about the database and it shouldn’t. It should be the other
way around. Whereas in clean architecture, dependencies go to the business (inner) layer and abstraction also rises towards the business layer, so they match nicely.
This is important because abstraction is the theory and dependencies are the practice.
Abstraction is the logical layout of the app, and dependencies are how it is actually
composed together. In clean architecture these two match up, while in the standard
three-tier architecture they don’t; and this can quickly lead to all kinds of logical inconsistencies and mess if you aren’t careful.
PART 2: THE CLEAN ARCHITECTURE
III. Communication between layers
Now that we have divided our app into the modules, nicely separated everything, put
business logic in the center of our app and implementation details in the outskirts, everything looks great. But you’ve probably quickly run into an interesting problem.
If your UI is an implementation detail, the internet is an implementation detail, and business logic is in between, how the heck can we fetch the data from the internet, pass it
through the business logic and then send it to the screen?
Business logic is in the middle and should mediate between the internet and the UI, but
it doesn’t even know that those two guys exist. This is a question of communication and
15
data flow.
We would like the data to be able to flow from the outer layers to the inner ones and
vice versa, but the dependency rule doesn’t allow that. Let’s strip that down to the simplest example.
We have only two layers, the green one and the red one. The green one is outer and
knows about the red one, and the red one is inner and knows only about itself. We want
the data to flow from the green one to the red one and back to the green one. The solution has already been hinted at before and is shown in the following diagram:
The part of the diagram at the bottom right side shows the data flow. Data goes from
the controller, through the use case (or replace use case with the component of your
choice) input port, then through the use case itself, and after that through the use case
PART 2: THE CLEAN ARCHITECTURE
output port back to the presenter.
Arrows on the main part of the diagram denote composition and inheritance—composition indicated by a filled arrowhead, inheritance an empty arrowhead. Composition
is also known as has-a relationship, and inheritance is-a relationship. “I” and “O” in the
circles represent input and output ports. It can be seen that a controller defined in the
green layer has an input port defined in the red layer. Use case (gears, business logic,
whatever—not important now) is (or implements) that input port and has an output
port. And finally, the presenter defined in the green layer is actually an output port defined in the red layer.
16
We can match that to the dataflow now. The controller has an input port—it literally
has a reference to it. It calls a method on it, so that data goes from the controller to the
input port. But the input port is an interface, and the actual implementation is the use
case: so it has called a method on a use case and the data flows to the use case. Use
case does something and wants to send the data back. It has a reference to the output
port—as the output port is defined in the same layer—so it can call the method on it.
Hence, data goes to the output port. And finally, the presenter is, or implements, the
output port; that’s the magic part. As it implements the output port, the data actually
flows into it.
The trick is that the use case knows only its output port; the world ends at this output
port. It’s up to the presenter to implement it. It could’ve been implemented by anything,
as use case doesn’t know or care, and is aware only of its little world inside its layer. We
can see that, by combining composition and inheritance, we can make the data flow in
both directions, although inner layers aren’t aware that they are communicating with the
outside world. Take a quick glance at the following diagram:
PART 2: THE CLEAN ARCHITECTURE
You can see that both has-a and is-a arrows point to the middle—the same as dependency arrows. Well, it’s logical. According to the dependency rule, it’s the only possible
way. The outer layer can see the inner layer, but not the other way around. The only
tricky part is that is-a relationship, although it points to the middle, reverses the dataflow.
17
Notice that it’s the inner layer’s responsibility to define its input and output ports, so
that the outer layers can use them to establish communication with it. I’ve said that
this solution has already been hinted at before, and it has. The notifications example
that illustrated abstraction is also an example of this kind of communication. We have
a notifications interface defined in the inner layer that business logic can use to show
notifications to the user, but we also have an implementation defined in the outer layer.
In that case, the notifications interface is business logic’s output port, which it uses to
communicate to the outer world—to the concrete implementation in this example. You
don’t have to name your classes FooOutputPort or BarInputPort; we name the ports just
to explain the theory.
Conclusion
So, is this overly complicated, overly obscured over-engineering? Well, it’s simple when
you get used to it. And it’s necessary. It allows us to make that nice abstraction / dependency match actually communicate and work in the real world. Maybe all of this
reminds you of the string theory: beautiful, theoretically elegant, but overly complicated
and we still don’t know whether it works, but in our case – it does. :)
So that’s it for the second part of the series. The last, third part, after all that we have
learned about the theory and architecture, will cover all you need to know about labels
on those diagrams, or in other words – separate components. We will show you a real
life clean architecture applied on Android.
PART 2: THE CLEAN ARCHITECTURE
18
PART 3
PART 3: APPLYING CLEAN ARCHITECTURE ON ANDROID
19
Applying clean
architecture on
Android
Tomislav Homan
So far in this series, we’ve covered some beginner’s mistakes and gone through the
clean architecture. In this last part, we will cover the last piece of the puzzle: labels, or
more precisely, components.
First, I’ll remove the stuff we don’t use on Android projects and I’ll add some stuff that
we do use but that isn’t found in the original Uncle Bob’s diagram. It looks like this:
I’ll go from the most abstract center to the edges.
Entities
Entities, aka domain objects or business objects, are the core of the app. They represent
the main functionality of the app, and you should be able to tell what the app is about
just by looking at them. They contain business logic, but it’s constrained to them only—
validation and stuff like that. They don’t interact with the gritty outside-world details,
and they also don’t handle persistence. If you had a news app, the entities would be
PART 3: APPLYING CLEAN ARCHITECTURE ON ANDROID
Category, Article, and Commercial, for example.
Use cases
Use cases, aka interactors, aka business services, are an extension of the entities, an
extension of the business logic, that is. They contain the logic that isn’t constrained
only to one entity but handle more of them. An indicator of a good use case is that you
can describe what it does in a simple sentence using common language—for example,
“Transfer money from one account to another.” You can even use such nomenclature to
name the class, e.g., TransferMoneyUseCase.
Repositories
Repositories serve to persist the entities. It’s as simple as that. They are defined as in-
20
terfaces and serve as output ports for the use cases that want to perform CRUD opera-
tions on entities. Additionally, they can expose some more complex operations related
to persistence such as filtering, aggregating, and so on. Concrete persistence strategies,
e.g., database or the Internet, are implemented in the outer layers. You could name the
interface AccountRepository, for instance.
Presenters
Presenters do what you would expect them to do if you are familiar with the MVP pattern. They handle user interactions, invoke appropriate business logic, and send the data
to the UI for rendering. There is usually some mapping between various types of models
here. Some would use controllers here, which is fine. The presenter we use is officially
called the supervising controller. We usually define one or two presenters per screen,
depending on the screen orientation, and our presenters’ lifecycles are tied to that of a
view. One piece of advice: try to name your methods on a presenter to be technology
agnostic. Pretend that you don’t know what technology the view is implemented in. So,
if you have methods named onSubmitOrderButtonClicked and onUserListItemSelected
in the view, the corresponding presenter methods that handle those events could be
named submitOrder and selectUser.
Device
This component has already been teased before in the notifications (again) example. It
PART 3: APPLYING CLEAN ARCHITECTURE ON ANDROID
contains the implementations of the gritty Android stuff such as sensors, alarms, notifications, players, all kinds of *Managers, and so on. It is a two-part component. The first
part is the interfaces defined in the inner circles that business logic uses as the output
port for communication with the outer world. The second part, and that’s drawn in the
diagram, are implementations of those interfaces. So, you can define, for example, interfaces named Gyroscope, Alarm, Notifications, and Player. Notice that the names are
abstract and technology agnostic. Business logic doesn’t care how the notification will
be shown, how the player will play the sound, or where the gyroscope data comes from.
You can make an implementation that writes the notifications to the terminal, write the
sound data to the log, or gather the gyroscope data from the predefined file. Such implementations are useful for debugging or creating a deterministic environment for you
to program in. But you will, of course, have to make implementations such as AndroidAlarm, NativePlayer, etc. In most cases, those implementations will just be wrappers
around Android’s manager classes.
21
DB & API
No philosophy here. Put the implementations of repositories in this component. All the
under-the-hood persistence stuff should be here: DAOs, ORM stuff, Retrofit (or something else) stuff, JSON parsing, etc. You can also implement a caching strategy here or
simply use in-memory persistence until you are done with the rest of the app. We had an
interesting discussion in the team recently. The question was this: Should the repository
expose methods such as fetchUsersOffline (fetchUsersFromCache) and fetchUsersOnline (fetchUsersFromInternet)? In other words, should business logic know where the
data comes from? Having read everything from this post, the answer is simple: no. But
there is a catch. If the decision about the data source is part of the business logic—if the
user can choose it, for example, or if you have an app with explicit offline mode—then
you CAN add such a distinction. But I wouldn’t define two methods for every fetch. I
would maybe expose methods such as enterOfflineMode and exitOfflineMode on the repository. Or if it applies to all the repositories, we could define an OfflineMode interface
with enter and exit methods and use it on the business logic side, leaving repositories to
query it for the mode and decide it internally.
UI
Even less philosophy here. Put everything related to the Android UI here. Activities, fragments, views, adapters, etc. Done.
PART 3: APPLYING CLEAN ARCHITECTURE ON ANDROID
22
Modules
The following diagram shows how have we divided all these components into Android
Studio modules. You might find another division more appropriate.
We group entities, use cases, repositories, and device interfaces into the domain module. If you want an extra challenge with a reward of eternal glory and a totally clean
design, you can make that module a pure Java module. It will prevent you from taking
shortcuts and putting something related to the Android here.
The device module should have everything related to Android that’s not data persistence and UI. The data module should hold everything related to data persistence, as
we’ve already said. You cannot make those two into Java modules because they need
access to various Android stuff. You can make them into Android library.
Finally, we group everything related to the UI (including presenters) into the UI module.
You can explicitly name it UI but because of all the Android stuff here, we leave it named
“app,” just as Android Studio named it during the creation of the project.
Is it better?
To answer that question, I’ll ditch Uncle Bob’s diagram and sprawl the components described before into a diagram like those we have used to rate previous types of architectures. After doing that, we get this:
PART 3: APPLYING CLEAN ARCHITECTURE ON ANDROID
23
Now let’s apply the same criteria that we have used on previous architectures.
It’s all neatly separated by module level, package level, and class level. So SRP should be
satisfied.
PART 3: APPLYING CLEAN ARCHITECTURE ON ANDROID
24
We have pushed Android and the real-world stuff as far out on the outskirts as we can.
Business logic doesn’t touch the Android directly anymore.
We have nicely separated classes that are easy to test. Classes touching the world can
be tested using Android test cases; the one not touching it can be tested using JUnit.
Someone malevolent would maybe call that class explosion. I call it testable. :)
PART 3: APPLYING CLEAN ARCHITECTURE ON ANDROID
It may be complicated – but it’s worth it
I hope that my carefully chosen criteria, which was not made up just to favor the clean
architecture, will convince you to give it a try. It seems complicated, and there are lots
of details here, but it’s worth it. Once you wire it all up, testing is much easier, bugs are
easier to isolate, new features are easy to add, code is more readable and maintainable,
everything works perfectly, the universe is satisfied.
So, this is it. If you still haven’t done so, look at the previous articles in the series: Mistakes and Clean Architecture. If you have any comments or questions, leave feedback
25
below. We are always interested in hearing what you think.
PART 4
PART 4: APPLYING CLEAN ARCHITECTURE ON ANDROID, HANDS ON ON
Applying Clean
Architecture on
Android, Hands on
(source code included)
Mihael Franceković
In the last part of our Android Architecture series, we adjusted Clean Architecture a bit
to the Android platform. We separated Android and the real world from our business
logic, satisfied stakeholders and made everything easily testable.
The theory is great, but where do we start when we create a new Android project? Let’s
get our hands dirty with clean code and turn that blank canvas into an architecture.
Foundations
We will lay down the foundations first – create modules and establish dependencies
26
among them to be on par with the dependency rule.
Those will be our modules, in order from the most abstract one to the concrete implementations:
1. Domain
Entities, use cases, repositories interfaces, and device interfaces go into the domain
module.
Ideally, entities and business logic should be platform agnostic. To feel safe, and to prevent us from placing some android stuff in here, we will make it a pure java module
2. Data
The data module should hold everything related to data persistence and manipulation.
Here we will find DAOs, ORMs, SharedPreferences, network related stuff like Retrofit
services and similar.
3. Device
The device module should have everything related to Android that’s not data persistence and UI. In example, wrapper classes for ConnectivityManager, NotificationManager and misc sensors.
PART 4: APPLYING CLEAN ARCHITECTURE ON ANDROID, HANDS ON ON
We will make both Data and Device modules android modules, as they must know about
Android and cannot be pure java.
4. The easiest part, app module (UI module)
This module is already created for you by the Android studio when you create a project.
Here you can place all the classes related to the Android UI such as presenters, controllers, view models, adapters and views.
Dependencies
Dependency rule defines that concrete modules depend on the more abstract ones.
You might remember from the third part of this series that UI (app), DB – API (data) and
Device (device) stuff is together in the outer ring. Meaning that they are on the same
abstraction level. How do we connect them together then?
Ideally, these modules would depend only on the domain module. In that case, dependencies would look somewhat like a star:
27
But, we are dealing with Android here and things just cannot be perfect. Because we
need to create our object graph and initialize things, modules sometimes depend on an
another module other than the domain.
For example, we are creating object graph for dependency injection in the app module.
PART 4: APPLYING CLEAN ARCHITECTURE ON ANDROID, HANDS ON ON
28
That forces app module to know about all of the other modules.
Our adjusted dependencies graph:
Bricks, a lot of bricks
Finally, it’s time to write some code. To make things easier, we will take an RSS Reader
app as an example. Our users should be able to manage their RSS feed subscriptions,
fetch articles from a feed and read them.
Domain
let’s start with the domain layer and create our core business models and logic.
Our business models are pretty straightforward:
– Feed – holds RSS feed related data like the url, thumbnail URL, title and description
– Article – holds article related data like the article title, url and publication date
And for our logic, we will use UseCases, aka. Interactors. They encapsulate small parts of
business logic in concise classes. All of them will implement general UseCase contract:
public interface UseCase<P> {
PART 4: APPLYING CLEAN ARCHITECTURE ON ANDROID, HANDS ON ON
29
interface Callback {
void onSuccess();
void onError(Throwable throwable);
}
void execute(P parameter, Callback callback);
}
The very first thing our users will do when they open our app is add a new RSS feed
subscription. So to start with our interactors, we will create AddNewFeedUseCase and
its helpers to handle feed addition and validation logic.
AddNewFeedUseCase will use FeedValidator to check feed URL validity, and we will also
create the FeedRepository contract which will provide our business logic some basic
CRUD capabilities to manage feed data:
public interface FeedRepository {
int createNewFeed(String feedUrl);
List<Feed> getUserFeeds();
List<Article> getFeedArticles(int feedId);
boolean deleteFeed(int feedId);
}
Note how our naming in the domain layer clearly propagates the idea of what our app is
doing.
Put everything together, our AddNewFeedUseCase looks like this:
public final class AddNewFeedUseCase implements UseCase<String> {
private final FeedValidator feedValidator;
PART 4: APPLYING CLEAN ARCHITECTURE ON ANDROID, HANDS ON ON
private final FeedRepository feedRepository;
@Override
public void execute(final String feedUrl, final Callback callback) {
if (feedValidator.isValid(feedUrl)) {
onValidFeedUrl(feedUrl, callback);
} else {
callback.onError(new InvalidFeedUrlException());
}
}
private void onValidFeedUrl(final String feedUrl, final Callback callback) {
try {
feedRepository.createNewFeed(feedUrl);
callback.onSuccess();
} catch (final Throwable throwable) {
callback.onError(throwable);
}
}
}
*Constructors are omitted for the sake of brevity.
30
Now, you might be wondering, why is our use case, as well as our callback, an interface?
To demonstrate our next problem better, let’s investigate GetFeedArticlesUseCase.
It takes a feedId -> fetches feed articles via FeedRespository -> returns feed articles
Here comes the data flow problem, the use case is in between presentation and data
layer. How do we establish communication between layers? Remember those input and
output ports?
PART 4: APPLYING CLEAN ARCHITECTURE ON ANDROID, HANDS ON ON
Our use case must implement the input port (interface). Presenter calls the method on
the use case, and the data flows to the use case (feedId). Use case maps feedId to feed
articles and wants to send them back to the presentation layer. It has a reference to the
output port (Callback), as the output port is defined in the same layer, so it calls a method on it. Hence, data goes to the output port – presenter.
We will tweak our UseCase contracts a bit:
public interface UseCase<P, R> {
interface Callback<R> {
interface Callback {
void onSuccess(R return);
void onSuccess();
void onError(Throwable throwable);
}
31
public interface CompletableUseCase<P> {
void onError(Throwable throwable);
}
}
void execute(P parameter, Callback<R> callback);
void execute(P parameter, Callback callback);
}
UseCase interfaces are Input ports, and Callback interfaces are output ports.
GetFeedArticlesUseCase implementation is as follows:
class GetFeedArticlesUseCase implements UseCase<Integer, List<Article>> {
private final FeedRepository feedRepository;
@Override
public void execute(final Integer feedId, final Callback<List<Article>> callback) {
try {
callback.onSuccess(feedRepository.getFeedArticles(feedId));
} catch (final Throwable throwable) {
callback.onError(throwable);
}
}
}
One last thing to note in the domain layer is that Interactors should only contain business logic. And in doing so, they can use repositories, combine other interactors and use
some utility objects like FeedValidator in our example.
PART 4: APPLYING CLEAN ARCHITECTURE ON ANDROID, HANDS ON ON
UI
Awesome, we can fetch articles, let’s show them to the user now.
Our view has a simple contract:
interface View {
void showArticles(List<ArticleViewModel> feedArticles);
void showErrorMessage();
void showLoadingIndicator();
}
Presenter for that view has a very simple presentation logic. It fetches articles, maps
them to the view models and passes on to the view, simple, right?
32
Simple presenters are yet another feat of the clean architecture and presentation-business logic separation.
Here is our FeedArticlesPresenter:
class FeedArticlesPresenter implements UseCase.Callback<List<Article>> {
private final GetFeedArticlesUseCase getFeedArticlesUseCase;
private final ViewModeMapper viewModelMapper;
public void fetchFeedItems(final int feedId) {
getFeedArticlesUseCase.execute(feedId, this);
}
@Override
public void onSuccess(final List<Article> articles) {
getView().showArticles(viewModelMapper.mapArticlesToViewModels(articles));
}
@Override
public void onError(final Throwable throwable) {
getView().showErrorMessage();
}
}
PART 4: APPLYING CLEAN ARCHITECTURE ON ANDROID, HANDS ON ON
33
Note that FeedArticlesPresenter implements Callback interface and it passes itself to the
use case, it is actually an output port for the use case and in that way it closes the data
flow. This is the concrete example of the data flow that we mentioned earlier, we can
tweak labels on the flow diagram to match this example:
Where our parameter P is integer feedId, and return type R is a list of the Articles.
You do not have to use presenters to handle presentation logic, we could say that Clean
architecture is “frontend” agnostic – meaning you can use MVP, MVC, MVVM or anything
else on top of it.
Let’s throw some rx in the mix
Now, if you were wondering why there is such hype about RxJava, we will take a look at
the reactive implementation of our use cases:
public interface UseCase<P, R> {
Single<R>
Completable execute(P parameter);
(P parameter);
}
}
PART 4: APPLYING CLEAN ARCHITECTURE ON ANDROID, HANDS ON ON
34
public interface CompletableUseCase<P> {
Callback interfaces are now gone and we use rxJava Single/Completable interface as
our output port.
Reactive implementation of the GetFeedArticlesUseCase:
class GetFeedArticlesUseCase implements UseCase<Integer, List<Article>> {
private final FeedRepository feedRepository;
@Override
public Single<List<Article>> execute(final Integer feedId) {
return feedRepository.getFeedArticles(feedId);
}
}
And reactive FeedArticlePresenter is as follows:
class FeedArticlesPresenter {
private final GetFeedArticlesUseCase getFeedArticlesUseCase;
private final ViewModeMapper viewModelMapper;
public void fetchFeedItems(final int feedId) {
getFeedItemsUseCase.execute(feedId)
.map(feedViewModeMapper::mapFeedItemsToViewModels)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this::onSuccess, this::onError);
}
private void onSuccess(final List articleViewModels) {
getView().showArticles(articleViewModels);
}
private void onError(final Throwable throwable) {
getView().showErrorMessage();
}
}
PART 4: APPLYING CLEAN ARCHITECTURE ON ANDROID, HANDS ON ON
Although it’s a bit hidden, the same data flow inversion principle still holds, because
without RxJava presenters were implementing the callback, and with RxJava subscribers are also contained in the outer layer – somewhere in the presenter.
Data and Device
Data and Device modules contain all of the implementation details that business logic
does not care about. It only cares about the contracts, allowing you to easily test it and
swap out implementations without touching the business logic.
Here you can use your favorite ORMs or DAOs to store data locally and network services
to fetch data from the network. We will implement FeedService to fetch articles, and use
FeedDao to store articles data on the device.
Each data source, both network, and local storage, will have its own models to work with.
In our example, they are ApiFeed – ApiArticle and DbFeed – DbArticle.
35
Concrete implementation of the FeedRepository is found in the Data module as well.
Device module will hold implementation of the Notifications contract that is a wrapper
around the NotificationManager class. We could perhaps use Notifications from our business logic to show the user a notification when there are new articles published in which
the user might be interested in and drive engagement.
Models, models everywhere.
You might have noticed that we mentioned more models than just entities or business
models.
In reality, we also have db models, API models, view models and of course, business
models.
It is a good practice for every layer to have its own model that it works with, so your
concrete details, such as views, do not depend on the specific details of your lower layer
implementations. This way, you won’t have to break unrelated code if you, for example,
decide to change from one ORM to another.
To make that possible, it is necessary to use object mappers in each layer. In the examPART 4: APPLYING CLEAN ARCHITECTURE ON ANDROID, HANDS ON ON
36
ple, we used ViewModelMapper to map domain Article model to the ArticleViewModel.
Conclusion
Following these guidelines, we created a robust and versatile architecture. At first, it
may seem like a lot of code, and it kind of is, but remember that we are building our architecture for the future changes and features. And if you do it correctly, future you will
be thankful.
In the next part, we will cover maybe the most important part of this architecture, its
testability and how to test it out.
So, in the meantime, what part of the architecture implementation did you find interesting the most?
PART 5
How to Test Clean
Architecture
David Geček
PART 5: HOW TO TEST CLEAN ARCHITECTURE
Why should you care about testing? Programmers, like any human, make mistakes. We
may forget about that edge case that we implemented last month, or about how some
method behaves when we pass it an empty string.
There is no point in taking an app after every change and trying every possible click,
tap, gesture and orientation change just to be sure that everything works. And you’ll
probably forget about that triple tap in the right top corner while rotating the device so
everything will crash when the user does it and something throws null pointer exception.
Users do silly stuff, and we need to make sure that every class does what it’s supposed
to and that every part of the app handles everything we throw at it.
That’s why we write automated tests.
37
1. Testing clean architecture
Clean architecture is all about maintainability and testability. Every part of the architecture has exactly one purpose. We just need to specify it and check that it actually does
its job every time.
Now, let’s be realistic. What can we actually test? Everything. Genuinely, if you structure
your code right, you can test everything. It’s up to you what to test. Unfortunately, there
is usually no time to test everything.
Testability. That’s the first step. The second step is testing the right way. Let’s remind us
about the old rule of FIRST:
Fast – Tests should be really fast. Part of the second fast. There is no point in writing tests if
it takes minutes or hours to execute them. No one will check tests if that’s the case!
Isolated – Test one unit of the app at the time. Arrange everything on that unit to behave exactly how you want and then poke the testing unit. Assert that it behaves correctly.
Repeatable – Test should have the same results every time it is executed. It should not
depend on some nondeterministic data.
Self-validating – Framework should know if test has passed or not. There should not be
any manual checking of tests. Just check if everything is green and that’s it :)
Timely – Tests should be written about the same time as the code, or even before the code!
So, we made a testable app, and we know how to test. What about tests’ names?
PART 5: HOW TO TEST CLEAN ARCHITECTURE
2. Naming tests
Is it important how we name the tests, honestly? Well, it has more indirect than direct
value. It reflects your attitude towards the tests, your way of thinking what to test.
Let’s meet our victim:
public final class DeleteFeedUseCase implements CompletableUseCaseWithParameter {
@Override
public Completable execute(final Integer feedId) {
//implementation
}
}
38
First, naive way would be to write tests like this:
@Test
public void executeWhenDatabaseReturnsTrue() throws Exception {
}
@Test
public void executeWithErrorInDatabase() throws Exception {
}
This is called implementation-style naming. It is tightly coupled with class implementation. When we change the implementation, we need to change what we expect from the
class. These are usually written after the code, and the only good thing about them is
that they are written quickly.
The second way is example-style naming:
@Test
public void doSomethingWithIdsSmallerThanZero() throws Exception {
}
PART 5: HOW TO TEST CLEAN ARCHITECTURE
39
@Test
public void ignoreWhenNullIsPassed() throws Exception {
}
Example-style tests are examples of system usage. They are nice when testing edge cases, but don’t use them for everything, they are too coupled with the implementation.
Now, let’s try to abstract our view of this class and move away from the implementation.
What about this:
@Test
public void shouldDeleteExistingFeed() throws Exception {
}
@Test
public void shouldIgnoreDeletingNonExistingFeed() throws Exception {
}
We know exactly what we expect from this class. This test class can be used as class’
specification, hence the name – specification-style naming. Name does not say anything
about the implementation, and from the tests’ names – specification – we can write the
actual concrete class. Specification-style names are usually the best way to go, but if
you think you cannot test some implementation specific edge cases, you can always
throw in few example-style tests.
Theory ends here and we are ready to get our hands dirty!
3. Testing domain
Let’s see how can we test use cases. Structure of use cases in our Reedly app looks like this:
PART 5: HOW TO TEST CLEAN ARCHITECTURE
Problem is that EnableBackgroundFeedUpdatesUseCase is final and if it’s mock is needed for some other use case testing, it cannot be done. Mockito doesn’t allow mocking of
final classes.
Use cases are referenced by its implementation, so let’s add another layer of interfaces:
40
Now we can mock EnableBackgroundFeedUpdatesUseCase interface. But in our everyday practice we concluded that this is very confusing while developing, middle layer
interfaces are empty and use cases don’t actually need to have interfaces. Use cases do
only one job and it says so right in the name – “enable background feed updates use
case”, there is nothing to abstract!
OK, let’s try this – we don’t need to make use cases final.
We try to make everything that we can final, it makes more structured and more optimized code. We can live with use cases not being final, but there must be a better way.
We came to the solution to use mockito-inline. It makes the unmockable, mockable.
With new versions of the Mockito, it is possible to enable mocking of the final classes.
PART 5: HOW TO TEST CLEAN ARCHITECTURE
Here is example of use case implementation:
public final class EnableBackgroundFeedUpdatesUseCase implements CompletableUseCase {
private final SetShouldUpdateFeedsInBackgroundUseCase setShouldUpdateFeedsInBackgroundUseCase;
private final FeedsUpdateScheduler feedsUpdateScheduler;
//constructor
@Override
public Completable execute() {
return setShouldUpdateFeedsInBackgroundUseCase.execute(true)
.concatWith(Completable.fromAction(feedsUpdateScheduler::scheduleBackgroundFeedUpdates));
}
41
}
When testing use cases we should test that use case calls correct methods in repositories or executes other use cases. We should also test that use case returns proper callback:
private EnableBackgroundFeedUpdatesUseCase enableBackgroundFeedUpdatesUseCase;
private SetShouldUpdateFeedsInBackgroundUseCase setShouldUpdateFeedsInBackgroundUseCase;
private FeedsUpdateScheduler feedUpdateScheduler;
private TestSubscriber testSubscriber;
@Before
public void setUp() throws Exception {
setShouldUpdateFeedsInBackgroundUseCase = Mockito.mock(SetShouldUpdateFeedsInBackgroundUseCase.class);
feedUpdateScheduler = Mockito.mock(FeedsUpdateScheduler.class);
testSubscriber = new TestSubscriber();
enableBackgroundFeedUpdatesUseCase = new EnableBackgroundFeedUpdatesUseCase(setShouldUpdateFeedsInBackgroundUseCase, feedUpdateScheduler);
}
@Test
public void shouldEnableBackgroundFeedUpdates() throws Exception {
Mockito.when(setShouldUpdateFeedsInBackgroundUseCase.execute(true)).thenReturn(Completable.complete());
enableBackgroundFeedUpdatesUseCase.execute().subscribe(testSubscriber);
Mockito.verify(setShouldUpdateFeedsInBackgroundUseCase, Mockito.times(1)).execute(true);
PART 5: HOW TO TEST CLEAN ARCHITECTURE
42
Mockito.verifyNoMoreInteractions(setShouldUpdateFeedsInBackgroundUseCase);
Mockito.verify(feedUpdateScheduler, Mockito.times(1)).scheduleBackgroundFeedUpdates();
Mockito.verifyNoMoreInteractions(feedUpdateScheduler);
testSubscriber.assertCompleted();
}
Here TestSubscriber from Rx is used, so proper callback can be tested. It can assert
completion, emitted values, number of values etc.
4. Testing the data
Here is very simple repository method, it just uses one DAO method:
public final class FeedRepositoryImpl implements FeedRepository {
private final FeedDao feedDao;
private final Scheduler backgroundScheduler;
//constructor
@Override
public Single feedExists(final String feedUrl) {
return Single.defer(() -> feedDao.doesFeedExist(feedUrl))
.subscribeOn(backgroundScheduler);
}
//more methods
}
When testing repositories, you should arrange DAOs – make them return or receive
some dummy data, and check that repository is handling the data in a proper way:
private FeedService feedService;
private FeedDao feedDao;
private PreferenceUtils preferenceUtils;
private Scheduler scheduler;
PART 5: HOW TO TEST CLEAN ARCHITECTURE
private FeedRepositoryImpl feedRepositoryImpl;
@Before
public void setUp() throws Exception {
feedService = Mockito.mock(FeedService.class);
feedDao = Mockito.mock(FeedDao.class);
preferenceUtils = Mockito.mock(PreferenceUtils.class);
scheduler = Schedulers.immediate();
feedRepositoryImpl = new FeedRepositoryImpl(feedService, feedDao, preferenceUtils, scheduler);
}
@Test
public void shouldReturnInfoAboutFeedExistingIfFeedExists() throws Exception {
Mockito.when(feedDao.doesFeedExist(DataTestData.TEST_COMPLEX_URL_STRING_1)).thenReturn(Single.just(true));
43
final TestSubscriber testSubscriber = new TestSubscriber<>();
feedRepositoryImpl.feedExists(DataTestData.TEST_COMPLEX_URL_STRING_1).subscribe(testSubscriber);
Mockito.verify(feedDao, Mockito.times(1)).doesFeedExist(DataTestData.TEST_COMPLEX_URL_STRING_1);
Mockito.verifyNoMoreInteractions(feedDao);
testSubscriber.assertCompleted();
testSubscriber.assertValue(true);
}
When testing mappers (converters), specify input to the mapper, and exact output you
expect from the mapper, then assert they are equal. Do the same for services, parsers etc.
5. Testing the app module
On top of the clean architecture, we like to use MVP. Presenters are just ordinary Java
objects, not connected with Android, so there is nothing special about testing them.
Let’s see what can we test:
public final class ArticlesPresenterTest {
@Test
public void shouldFetchArticlesAndPassThemToView() throws Exception {
}
@Test
public void shouldFetchFavouriteArticlesAndPassThemToView() throws Exception {
}
PART 5: HOW TO TEST CLEAN ARCHITECTURE
@Test
public void shouldShowArticleDetails() throws Exception {
}
@Test
public void shouldMarkArticleAsRead() throws Exception {
}
@Test
public void shouldMakeArticleFavourite() throws Exception {
}
@Test
public void shouldMakeArticleNotFavorite() throws Exception {
}
}
44
Presenters usually have a lot of dependencies. We inject dependencies into presenters
via @Inject annotation, not via the constructor. So in tests below, we need to use @Mock
and @Spy annotations:
public final class ArticlesPresenter extends BasePresenter implements ArticlesContract.Presenter {
@Inject
GetArticlesUseCase getArticlesUseCase;
@Inject
FeedViewModeMapper feedViewModeMapper;
// (...) more fields
public ArticlesPresenter(final ArticlesContract.View view) {
super(view);
}
@Override
public void fetchArticles(final int feedId) {
viewActionQueue.subscribeTo(getArticlesUseCase.execute(feedId)
.map(feedViewModeMapper::mapArticlesToViewModels)
.map(this::toViewAction),
Throwable::printStackTrace);
}
// (...) more methods
}
PART 5: HOW TO TEST CLEAN ARCHITECTURE
45
@Mock just makes plain mock out of the class. @Spy let’s you use the existing instance
with all of the methods working, but you can mock some methods and “spy” which
methods are called. Mocks are injected into the presenter via @InjectMocks annotation:
@Mock
GetArticlesUseCase getArticlesUseCase;
@Mock
FeedViewModeMapper feedViewModeMapper;
@Mock
ConnectivityReceiver connectivityReceiver;
@Mock
ViewActionQueueProvider viewActionQueueProvider;
@Spy
Scheduler mainThreadScheduler = Schedulers.immediate();
@Spy
MockViewActionQueue mockViewActionHandler;
@InjectMocks
ArticlesPresenter articlesPresenter;
Then some setting up is required. View is mocked manually because it is injected via
constructor and we call presenter.start() and presenter.activate() so presenter is prepared and started:
@Before
public void setUp() throws Exception {
view = Mockito.mock(ArticlesContract.View.class);
PART 5: HOW TO TEST CLEAN ARCHITECTURE
46
articlesPresenter = new ArticlesPresenter(view);
MockitoAnnotations.initMocks(this);
Mockito.when(connectivityReceiver.getConnectivityStatus()).thenReturn(Observable.just(true));
Mockito.when(viewActionQueueProvider.queueFor(Mockito.any())).thenReturn(new MockViewActionQueue ());
articlesPresenter.start();
articlesPresenter.activate();
}
When everything is ready, we can start writing the tests. Prepare everything and make
sure that the presenter calls the view if needed:
@Test
public void shouldFetchArticlesAndPassThemToView() throws Exception {
final int feedId = AppTestData.TEST_FEED_ID;
final List<article> articles = new ArrayList<>();
final Article = new Article (AppTestData.TEST_ARTICLE_ID, feedId, AppTestData.TEST_
STRING, AppTestData.TEST_LINK, AppTestData.TEST_LONG_DATE,
false, false);
articles.add(article);
final List<ArticleViewModel articleViewModels = new ArrayList <>();
final ArticleViewModel articleViweModel = new ArticleViewModel(AppTestData.TEST_ARTICLE_
ID, AppTestData.TEST_STRING, AppTestData.TEST_LINK, AppTestDAta.TEST_STRING,
false, false);
articleViewModels.add(articleViewModel);
Mockito.when(getArticlesUseCase.execute(feedID)).thenReturn(Single.just(articles));
Mockito.when(feedViewModeMapper.mapArticlesToViewModels(Mockito.anyList())).thenReturn(articleViewModels);
articlesPresenter.fetchArticles(feedId);
Mockito.verify(getArticlesUseCase, Mockito.times(1)).execute(feedId);
Moclito.verify(view, Mockito.times(1)).showArticles(articleViewModels);
}
PART 5: HOW TO TEST CLEAN ARCHITECTURE
47
Conclusion
Think about testing before and during coding. That way you can write testable and
decoupled code. Use your tests as class specification, and if possible write them before
the code. Don’t let your ego get in the way, we all make mistakes. Therefore, we need to
have a process to defend the application from ourselves!
Be sure to check our other Android Architecture post below: Android Architecture Part
4: Applying Clean Architecture on Android,
Download