Test First, ask questions later…


I’ve been doing this test first thing for a while and now I’m in a position where I’m teaching others how to properly design code with this practice. I’m using it in my current project and I’ve also been hanging out in the code newbie community answering questions and confusing others. I usually respond to arbitrary questions with something hinting to the need to write test code. Here’s some typical exchanges I have throughout my week:

Them: “I can’t find the source to this null pointer exception…”

Me: “Can you isolate the issue with a failing test?”

Them: “My REST call is returning 403”

Me: “You know you can use Selenium in these scenarios to…”

Them: “I think this method is going to be slow.”

Me: “Do you have a failing performance test that indicates we should optimize here?”

Them: “You have a few dollars? I forgot to pack a lunch…”

Me: “I usually have a checklist I refer to prior to leaving the house for things like my employee badge, my lunch, etc. I highlight each item with a green bar prior to jumping in the car…”

My Kids: “Daddy, I don’t feel well. Can I stay home today?”

Me: “I thought I told you about going outside without proper test coverage! You probably got a regression of your symptoms from last week!”

Needless to say it’s a very important topic and always forefront on my mind. I lead with a test case. One person in particular drew an interest and asked a basic question about unit testing. Not paying attention to who it was I sorta went all out in my explanation without really understanding the level of expertise. Usually I get the typical response of, “You’re a bit extreme, bro! Calm down!” She was not intimidated at all. Instead, she was rather appreciative, which I’m thankful for because I really went full throttle in my response. I don’t really have an off switch when I get on that topic so I ended up writing roughly 2 pages of text before I came up for air. The question was something along the lines of, “how do I test my method that calculates my layout dimensions based on screen size?” My brain jumped immediately to “Test First!” without asking any pause to ask questions about the project, deadline, or backstory. In short, here’s what I attacked with. (Yes it was an unsolicited attack on an innocent bystander but I have no filter when it comes to these things):

See there are 3 levels of testing as I like to see it.
* Functional testing
* Integration testing
* Unit testing

[8:13]
Each level sits on top of the levels beneath it and has certain characteristics.

[8:36 AM]
Functional testing is the type testing where you test the app in its entirety end to end for broad functionality.

[8:37]
This involves testing the app in its deployed environment (device or emulator) and may or may not be automated by tools.

[8:38]
Integration testing is where you test two or more components of your app in isolation. In these types of tests you are exercising the integration points of particular components. I.e. does objectA integrate and play nicely with objectB?

[8:39]
A component may be represented by something as coarse grained as a library or module, or something as fine grained as an object or a method.

[8:40]
Again, you are primarily testing to make sure the pieces work together.

[8:41]
Unit testing the the final level of testing. It is here where you test specific objects and/or methods in isolation.

[8:41]
Why am I telling you all of this?

[8:41]
(I’m so glad you asked!)

[8:42]
The thing is that each level should focus primarily on a specific type of testing.

[8:43]
With functional tests you test broad functionality. These should be very coarse grain tests and not too specific. These tests are a way of asking, “does my app function as a whole?”

[8:44]
They run on device and as a result they are slow and cumbersome. These are the tests that normally make use of Espresso, UIAutomator, etc.

[8:45]
If you try to test too much or test too specific of a scenario you burn most of your effort using the wrong test for the job and the test will always be brittle and unreliable. (edited)

[8:47]
Integration testing is more fine grained. These tests may or may not run on the device. Once again, they exercise the integration points between components. These tests answer the question, “does my component integrate or play nicely with this other component?”

[8:48]
With these tests you can be slightly more specific but you should still focus on integration. they should be coarse grained in general but can be slightly more fine grained than the functional tests.

[8:50]
That brings me to Unit tests. These are where your fine grained test scenarios live. whenever you are trying to find/fix a very specific bug or exercise a very specific scenario you can rely on these to give you what you need. This is where you would focus primarily to get 100% coverage on the behavior of everything.

[8:50]
I use a vacuum cleaner analogy (which may or may not make sense to you)…

[8:53]
You know how the vacuum cleaner has the big apparatus that sucks the dirt from the floor but this big thing can’t get into all those hard to reach places like in the corner by the door and waaay under the couch towards the back? Well the vacuum cleaner companies usually supply you with attachments like the edger piece, which can get in the tight corners, and the extension piece that lets you vacuum high or in long narrow places.

[8:54]
In the same way your functional tests are like the big apparatus on the vacuum. It’s used for general testing, and broad strokes but there are places that it will not reach.

[8:54]
You should make use of the lower levels of testing for these more specific scenarios. (edited)

[8:55]
Whew, that was a long answer to your specific problem…

[8:56]
To your issue you wrote:
“To test my calculation I thought I’d set a value for display height and width and see if the method returns the expected LayoutParams… I tried different approaches and gave up in the end because all that mocking of Android dependencies” (edited)

[8:57]
You approach indicates that you are attempt to test something very specific at a higher level than necessary.

[8:58]
To test your calculation you should have a unit test which, when given a dimension returns an expected size.

[9:00]
This test can be written in pure Java without android dependencies, which means it can run outside of the container under the JVM using JUnit.

[9:01]
I don’t know if you’ve ever used regular JUnit in your Android project but it something to look into.

[9:02]
What you can do is create a Java module in your project and place your Junit test code there along with the classes that these tests exercise.

I wasn’t finished there. I came back (after coffee) with an example:

Here’s an example. Say you have an app with a single button which, when pressed, makes a network call to a server to increment a counter.

[11:57]
The functional test would load the entire app on the device, deploy the server code to the cloud and launch it, start the app on the device and send a button press.

[11:58]
It would then verify the counter reflected on a text label on the view has been incremented.

[11:59]
(I forgot to mention that there is a textView in the example that shows the value of the counter.)

There are actually quite a few pieces in this seemingly basic example that you should consider

[12:01]
The initial value of the counter needs to be read from the server.

[12:02]
this involves an object that can make a network call to the server, an actual server, an actual network… etc.

[12:02]
A functional test would load two or more of these pieces and test them together.

[12:03]
you could write a test that says:
Given a CounterRequestor
when I ask for the count
then I should get some number

[12:05]
which in Java/JUnit equates to:
CounterRequestor requestor;

@test public void shouldReceiveANumber() {
Integer initialCount = requestor.getInitialCount();
assertEquals(initialCount, 0);
}

[12:06]
This test requires an INTERFACE not a concrete class because you are DESIGNING (the 2nd “D” in TDD) the intention of the app.

[12:07]
The interface would be implemented by some concrete object, say InternetServerCounterRequestor which you could instantiate inside your test in setup…

[12:08]
(also I would write the test before defining the Interface so I could get a feel for how I would use it. I usually use Intellij/AndroidStudio’s Alt+Enter combo to generate the interface after I’ve written a test that shows how I’d use it.) (edited)

[12:09]
then to run this integration test you would need only the CounterRequestor defined, the InternetServerCounterRequestor implementation and an actual running server.

[12:10]
That’s testing the InternetServerCounterRequestor the internet connectivity in your house or office, and the server in isolation without involving your app.

[12:11]
You could then write more tests the specify how you would interact with the count service running over the internet.

[12:12]
The important reason why you would use an interface here is because you want to be able to test pieces in isolation. So now let’s say you want to test the Activity without the internet and without the server deployed…

[12:13]
You could write a test that uses Roboelectric or something to load just the activity and mock the CounterRequestor.

[12:14]
in such a test you could test:
Given a MainActivity with a textField
When it is created
Then it should ask the CounterRequestor for the initial count

[12:16]
you could also test:
Given a MainActivity with a textField
And a mocked CounterRequestor
When it asks the CounterRequestor for an initial count
And the CounterRequestor returns the number 77
Then the textField should be equal to 77

[12:16]
There are a few other moving pieces here as well…

[12:16]
you have the basic button you could write tests for

[12:17]
Given a MainActivity with a button
And a mocked CounterRequestor
When I press the button
Then my mocked CounterRequestor should be asked to send a counter update message

[12:18]
these are all integration tests that test pieces of your solution in isolation

[12:19]
You could probably see a useful pattern here with this example…

[12:19]
The fact that we used an interface allows us to easily vary what/how the counter is implemented…

[12:20]
you could implement a PeerToPeer counter that uses bluetooth instead of HTTP to maintain a counter across devices.

[12:21]
because nowhere in your test code do we specify HTTP, URLs or anything related the change is localized to the implementation of the CounterRequestor

[12:21]
so then this brings me to unit testing…

[12:22]
going through this example (which I am honestly making up as I go along here) you will find that making the network request to the server to get a count might involve XML or JSON parsing…

[12:23]
because usually a web service would return something like:
{
initialCount:0,
error:""
}

[12:24]
instead of just
0

[12:25]
so then you might find yourself needing to do something like parse the response which gets into the micro level of “I have this JSON object with a few keys. I want to design an object that can read the initialCount key and give me its value,

[12:25]
you could do this with a unit test.

There is more where that came from but the above explanation should paint a rough picture of how I see the world. The person I was helping is a developer named Alina from Sweden, I believe. She maintains an interesting blog and has an even more interesting backstory coming from marketing into coding.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s