Mocking responses from server — Why?

widewallpapers.net

Programming is like geometry — there are multiple possible solutions to get the same results. This is beauty of programming as also its curse. Imagine one task and 20 programmers. Exactly 20 unique solutions. One better than the other. One is smart, the other convenient, next is inefficient, but does what it does and was developed in 5 mins instead of 3 days, there is also one that is super efficient, but only a machine can understand the code.

Which one do I prefer? I have no simple answer for that question. Depends on circumstances.

„DONE IS BETTER THAN PERFECT”

Everything depends on exact situation, sometimes a workaround is better than the proper fix, but workaround MUST always be considered as a temporary solution. This proper fix must arrive and must arrive quickly. A theory of broken window is real!

These temporary solutions, may fix one thing and break the other elsewhere in the application — without us realizing it.

Why we should mock responses for UI Tests?

For those who want to see the code directly, here’s the Mock Server for Android.

Basically, there is no person in the world that can click through entire application as fast as a machine, but also if you let 2 people to test your app, they most likely go through the same paths — repeating the same steps (main features of the app) — which costs the company twice as much. The risk is that they may never find a bug that was resolved 4 versions earlier.

Tests are companion code to our applications — but sometimes they are even more important.

  1. They are proof that our code works as expected
  2. They are perfect guardians of regression
  3. They are great story tellers — you take specification and you provide the step-by-step application behavior according to it. In such situation if client comes back to you and claims something was not supposed to work the way it works, this can mean only two things:
    • You didn’t understand the user’s story at the very beginning
    • Someone wants to change their mind out of charge

While the third scenario is more important for the company and the second sounds convincing the first not that much. It’s because there are many different things that can have influence on tests and make them pass and fail alternately.

That’s what we call „Flaky test” — a test that behavior is unexpected. Such test is not a help to us at all. Our code must work always, not mostly. Such test tells us that either our code works unpredictably or the test itself — what is a use of such test? None use at all!

Examples of such tests can be based on for instance:
• Real-time
• Multi-threading
• REST server communication — This article will focus on that point

Code change = Code crash

Our code will crash. It’s a matter of time. It will be our fault, our team’s or 3rd party’s. Have you ever met an application that never changed? No? This would be too perfect to implement something more complex than „hello world” and claim it will live for ever. Especially if you rely on back-end that also would have to be perfect, users would never complain or asked for new features.

We know that things will change, we have to prepare for refactor, but to start refactoring we have to make sure of few things:

  1. We must have stable, reliable tests
  2. By refactoring the application code, we must make sure we haven’t broken any test — broken test means we changed application behavior

Or if the whole UX changed we must:

  1. Write new UI test that crashes
  2. Make the test pass, by applying the application code

If we rely on live server, we may get responses too late and even though that application works as expected for user, it will break the test.

Can’t we just wait for responses from server?

Consider we rely on live server response. Imagine that we compare a number of stars (like stars on GitHub). We write the test that checks if there is a project:

  • named: Mock Server
  • starred: 7

One person more that starred our project on the internet and we have to adjust the test that fails — because 7 doesn’t equal 8 for our test.

You may ask „but why would we want to compare stars?”. Well, we want to know that we parse response correctly.


On the other hand, we want to know how our application reacts to responses other than just happy path (200). What if the server returns 404?

Do you really want to ask your buddy to change a REST response for you to return this 404 on an alpha server?
It takes his time to change the code and then it takes your time too to wait for deployment, just to check that error shows up on 404 response code.

Test Driven Development — call to rescue

Before you write any code you should translate specification to UI test.
Start with describing story:

User sees a list of projects after app launch
@Test public void seesListOfProjectsAndOneSpecificThatSheWasSearchingFor() {
// Betty opens application
// List of repositories showed up
// Among the projects Betty found the project that she was interested in named "Mock server"
}

Then you should add checking code that assures that this simple story is fulfilled.

@Test public void seesListOfProjectsAndOneSpecificThatSheWasSearchingFor() {
mockServer.rewriteResponse("users/octocat/repos", 200,
Repos.INSTANCE.listOfRepos());

// Betty opens application
activity.launchActivity(new Intent());

// List of repositories showed up
onView(withId(R.id.project_list))
.check(matches(isDisplayed()));

// Among the projects Betty found the project that she was interested in named "Mock server"
onView(withText(MOCK_SERVER))
.check(matches(isDisplayed()));
}

After this test passes, you might want to implement error handling in the app and display message to user (another user’s story — ):

User sees a box with info that something went wrong with server connection
@Test public void displaysErrorToUserOnServerError() {
// Bety opens the application
// An error occurs and application shows a message "Oops.. Something went wrong"
}

Then apply the code that will check it:

@Test public void displaysErrorToUserOnServerError() {
mockServer.rewriteResponse("users/octocat/repos", 404, "");

// Bety opens the application
activity.launchActivity(new Intent());

// An error occurs and application shows a message "Oops.. Something went wrong"
onView(withText("Oops.. Something went wrong"))
.check(matches(isDisplayed()));
}

Make sure that both of them pass.

For now on if someone changes error message or while resolving conflicts he forgets about the box, this test will inform you about it right away.

Conclusion

On daily basis I have to talk to my PM, my bosses and workmates that tests are important and these should be included in estimations.
Writing tests can take 30–50% more time, but they save you bug fixing after that, regression and TDD itself enforces you to work according to SOLID rules.


Checkout my other article about Test Driven Development. I’d love to hear your feedback!