Unit Testing in VIPER Architecture – Feasibility and Methods

androidarchitectural-patternsdesign-patternsios

TL;DR There are no methods in VIPER which are returning any value so how to test them?

Reasoning:

In VIPER, each layer is communicating with other layer by holding an abstract reference to it (Protocols/Interfaces).

For e.g, View has reference of a Presenter. If view calls presenter.getSomething() presenter will not return a value right away. Instead, presenter will have a reference of view, and after completing its operations presenter will call view.setSomething(something).

This flow is consistent across all the flows between the layers. And to paraphrase it, classes that implement these protocols/interfaces, will only expose the methods they implement from protocols and interfaces as public. While writing tests you can only access these public methods, and, all of these methods return null.

The argument that you can return a value is not valid enough in real life cases where operations that are performed after a call must be asynchronous, so it has to use VIPER like callback structure instead of directly returning a value upon call.

Yet, every blog post, article and video that explains VIPER claims that it makes code testable for TDD.

Edit:
There is a question unit testing void method which may seem to be the duplicate of this one, but my question is about the similar behaviour not how we test it, but how the architecture limits the approach (to my understanding atleast).

Best Answer

Oh it's possible. It just doesn't look like what you want it to look like.

What you're complaining about is the use of output ports1,2,3 to communicate results rather then returning results. Does this make testing more complicated? Yes. Then why do it? Because it gives you another layer of polymorphism.

When you return, you're returning to the caller. You have no choice. Nowhere else to go. That means the caller has to know how to deal with the result. If you have an output port you can send the result off to whatever knows how to listen to you. That is powerful.

Why is that powerful? Because you're always going forward. Always facing towards the abstraction. Using things the way they were meant to be used rather then sneaking up behind them. Abstractions work better when you're not hacking them.

To test this you have to know how design things that know how to listen to an output port. But you were going to do that anyway. So what's the problem?

The problem is so many people get caught up thinking structurally. "All I'm doing is testing that a method got called". No. You're testing that a message, that communicates a result, got sent. If, in this mini output port language, that looks like a method call, fine. "But that's an implementation detail!" No. That's part of the output port language. What anything does with that message is up to them. What gets done with it is the implementation detail.

"But it's such a pain to test!" Well yes it is. But you decided to use it so it's your own fault. "So I should never use it?" No, you should use it when it does something useful for you, when you understand it, and when you think it's worth the pain it causes.