My apps are mostly GUIs that communicate to a server for most of their information. If anything goes wrong it will usually be in the network call or making a wrong assumption about a JSON object.
Unit Tests are not good for these network-related and i/o related tasks, otherwise, they won't be called unit tests.
SO I am trying to gather the point of Unit Tests in my case. Why would I test if an Android button can click or an EditText can see what I type? I just don't understand the utility of implementing these tedious tests
private void initElements(){
placeButton = (Button) findViewById(R.id.currplace);
placeButton.setText(MainActivity.this.getString(R.string.findingLocation));
placeButton.setEnabled(false);
selectplaceLayout = (LinearLayout)findViewById(R.id.selectplaceLayout);
selectplaceLayout.setVisibility(View.GONE);
splash = (RelativeLayout)findViewById(R.id.splashbg);
infoLayout = (LinearLayout)findViewById(R.id.infoLayout);
}
if this above method passed, which all my activities run in onCreate, then I know the app works. A unit test of this would be a redundant time-consuming thing to create. Time-consuming because I am not familiar with all the methods in the jUnit and Android testing framework.
So, long story short, what's the point? Is there a particular way I should be thinking about these tests? All examples and tutorials I've seen so far only talk about the simplest examples, for the sake of brevity, but I cannot think of any practical uses for unit tests in a predominately client-server app.
What am I expected to discover by accessing the android views that I already know I declared and initialized? I must be thinking about this in a too limited way
so, insight appreciated
There are lots of facets in your question, but to my opinion - you probably don't need unit-tests in your project.
Unit tests really shine when you need lots of business logic to your project. In this case you probably want to divide your application into multiple layers (say, 3-tier architecture) to, among other, add some natural isolation for business-logic layer and cover it with safety net of unit tests.
This safety net covers your ass during refactor of the business layer, and that's one of the main things you what from unit tests (TDD can offer some nice extra side effects though).
However, it's not all unicorns and rainbows and unit-test may cost, and sometimes they cost a lot. Good unit tests are isolated (i.e. deal with small chunks of code). This means that you have to add layers of abstraction in order to put your classes under the test.
This may have positive affect on your system or negative one. Layering makes your system more flexible with cost of increased complexity.
Having that said - the value of the unit-tests is proportional to the amount of abstract business-logic you are going to introduce in your project. You may think of it also this way - if it is overkill to add abstract layers to your architecture - don't add unit-tests - they will only make things more complicated (architecture and build wise).
Based on your description - your typical app tend to be pretty much presentation layer for some external server-side. It does not that much except presenting information on android handset and transforms user actions to commands to server-side where main business logic gets done (controlling).
With this approach most of the code you probably write is related to "how to display this and that" or "how to signal server in this and that case". This sort of code is obviously heavily depend on platform and this mean that if you do want to put it under test you'll have to mock lots and lots of Android specific code\behavior.
Now, Android is somewhat specific platform. It was designed to be both performance optimized and allow developers to start and produce apps quickly. Often this means some amount of "swiss-knife" classes you have use\extend and generally this speeds up writing code, but mocking those classes can become a real hell. Not to mention that you have to have understanding of how platform works under the hood to make those mock useful. In other words overhead from making those test is going to be high.
Another thing that is wrong with testing presentation layers is that they tend to change much more dynamically than business layers. And, of course this mean that you'll have to refactor thee tests which adds even more overhead.
I have to say one thing about various utility/helper classes though. Even if these classes are belong to presentation layer and do depend Android code, but do some rather non-trivial logic and it is easy to mock and write unit tests for them, it might actually be a good idea to do this. However, if you do have lots of such code - this is might be a signal that you haven't designed your architecture/layering well, and need to rethink what you are doing.
In the end to answer your question you have to answer these questions first:
Will it be overdesign to add abstract layer that is separated from the platform to your application (seems like in your case it will)? If yes - do not use unit-tests - they will only slow you down. If no - do use them.
Are you going to refactor a lot? If that's large project with lots of code and thus maintenance - you probably will, so invest in layering and unit-tests (but, at a glance, it does not seem that this is your case). If that is not your case - do not bother with unit-tests and go fast.
Do you need to mock platform a lot to write your unit tests? If yes (seems to be your case) - don't write unit tests - they do not worth the effort.
Hope this will help.
UTesting in Android generally takes relevance when you are using some layering with a good architecture. Nowadays the most popular is Clean Arch, which 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.
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.
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.
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.
Leaving the architecture besides any good modular and decoupled design will contain business logic in functions and classes, and if those follow single responsibility principle (they should), then likely should be tested.
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.
Examples and expanded info: https://five.agency/android-architecture-part-5-test-clean-architecture/
from Android.Docs
For testing Android apps, you typically create these types of automated unit tests:
Local tests: Unit tests that run on your local machine only. These tests are compiled to run locally on the Java Virtual Machine (JVM) to minimize execution time. Use this approach to run unit tests that have no dependencies on the Android framework or have dependencies that can be filled by using mock objects.
Instrumented tests: Unit tests that run on an Android device or emulator. These tests have access to instrumentation information, such as the Context for the app under test. Use this approach to run unit tests that have Android dependencies which cannot be easily filled by using mock objects.
Note: Unit tests are not suitable for testing complex UI interaction events. Instead, you should use the UI testing frameworks, as described in Automating UI Tests.
Related
I've in the past year written an Android app without doing any automated testing at all. I'm now learning about testing in Android but I still have some questions I can't seem to find answers to online.
I have unit-tested my presenters and since they don't use any Android-classes it has been pretty straight forward to write these tests.
I have also written some UI tests for my view (Fragment) using Espresso, which I also feel like I have a good grip on.
What I don't really understand is if I should write unit tests for all the methods in my Fragments as well? The UI tests check all the possible user interactions that can be done. But my Fragments still have some basic methods that are called from the presenter after it has fetched some data from model. These methods in my Fragment are really simple, they take data from presenter and use it to fill a view or toggle visibility or something similar. Should I write unit tests for these types of methods? They seem really simple but I want to make sure I understand Android testing completely.
Thanks!
UI tests are always very brittle creatures. I once was called by my boss to check why the ui tests were failing. In the end it was because the testdevice was not connected by usb to the testserver anymore.
Also ui tests take a lot of time to run. So you have to think about what you want to achieve? When do you run your tests? Where do you want to run the tests
Where? Either use some kind of device lab, or if money is tight run tests on headless emulators that you create every time
What? Think about the big picture. Features You have already covered the logic of your presenters in your unit tests, now try to cover the flows in the view
When? If you have a big test suite, it may take hours to run. In some companies even days. Schedule the whole test suite as a nightly build. But make sure to add a reduced set of tests, the absolute core of your product to run after every commit
Unit tests are generally pointless on trivial methods. If you have a method that just unconditionally sets a visible flag on an item, what's there to really test?
Unit testing your models and presenters should cover most potential bugs. I wouldn't worry much about your views, especially since you're already doing UI tests. You might want to write tests to cover bugs that come up more than once, but apart from that your time is better spent elsewhere.
Your question is about test coverage. If you want to cover more of your codes, you should write tests for every method even fragment methods using mocking tools.
I have been going through source code of Volley and have found that for every class or interface there is a test implementation.
Is it preferable to write Test class for every other class than using Mockito to just mock objects?
A broad question but let's collect the underlying facts:
writing your own custom test "stubs" enables you to implement your "own" vision of "test support"
especially, you do not have any dependencies to a mocking framework
In other words: you decide to re-invent the wheel to a certain degree. That prevents you running into bugs other people put down, at the risk of making your own mistakes.
In that sense, this boils down to the old discussion "buy or make yourself".
When talking about test cases, there are various experts that suggest to not rely on mocking frameworks. So this is a common practice, but I think a "minority" one.
My personal two cents here: ideally, you should write production code that can be tested without any mocking framework. But that isn't always possible. And for those cases, you have one mocking framework in your toolbox. You know how to use that in a reasonable way to get your testing done. I would find it way too cumbersome to do all test stubs manually. A lot of effort for a relatively small gain.
Recently I’ve been rethinking my android architecture project, trying to adapt it to a more “clean architecture”, specifically, the kind of design suggested by “Uncle Bob”.
Which it involves several layers of abstractions, a nice isolation of responsibilities and a very strong dependency inversion achieved by dependency injection; which, ultimately, leads to a very decoupled-portable system. A perfect candidate to be tested by unit testing and integration testing.
In my android implementation I’ve ended up having three different modules or layers:
-domain: entities, interactors, presenters (pure java module)
-data: (acts as a repository to supply the data to the domain) (android library module)
-presentation: ui related stuff, fragments, activities, views, etc (android application module)
So, I’m trying to figure out what would be the best approach on the iOS ecosystem.
I’ve tried creating a project with multiple targets to achieve the same solution:
-domain: command line target (which seems very weird but I think is the most pure swift target available)
-data: cocoa touch framework
-presentation: cocoa touch framework
With this approach I can use these targets in the way I did with android modules. But the first caveat I’ve found it is that I need to add manually every new file to the dependent target.
But my knowledge is very limited in projects with multiple targets. I mean I’ve never created an iOS application with multiple targets. So I don’t know even if the solution would be use a framework (cocoa touch/cocoa) as a target instead of a command line module for the domain layer.
Any thought would be really appreciate.
Thanks!
Uncle Bob's Clean Architecture absolutely applies to iOS, Swift, and Obj-C. Architecture is language agnostic. Uncle Bob himself codes mostly in Java but in his talks he rarely mentions Java. All his slides do not even show any code. It is an architecture meant to be applied to any project.
Why am I so sure? Because I've studied MVC, MVVM, ReactiveCocoa, and Clean Architecture for 2 years. I like Clean Architecture the best, by far. I tested it by converting 7 Apple sample projects to using the Clean Architecture. I've used this approach exclusively for over a year. It works out better every time.
Some of the benefits are:
Find and fix bugs faster and easier.
Extract business logic from view controllers into interactors.
Extract presentation logic from view controllers into presenters.
Change existing behaviors with confidence with fast and maintainable unit tests.
Write shorter methods with single responsibility.
Decouple class dependencies with clear established boundaries.
We also added a router component so we can use multiple storyboards. No more conflicts.
Writing unit tests is greatly simplified too because I only need to test the methods at the boundaries. I don't need to test private methods. On top of that, I didn't even need any mocking framework because writing your own mocks and stubs becomes trivial.
I've written my experience for my last 2 years studying iOS architecture at Clean Swift I also put together some Xcode templates to generate all the Clean Architecture components to save a ton of time.
UPDATE - To answer #Víctor Albertos's question about dependency injection in the comment below.
This is a really great question and demands a long detailed answer.
Always keep the VIP cycle in mind. In this case, the doSomethingOnLoad() method is not a boundary method. Rather, it is an internal method invoked only within CreateOrderViewController. In unit testing, we test a unit's expected behavior. We give inputs, observe outputs, then compare the outputs with our expectations.
Yes, I could have made doSomethingOnLoad() a private method. But I chose not to. One of the goals of Swift is to make it easy for developers to write code. All the boundary methods are already listed in the input and output protocols. There is really no need to litter the class with extraneous private modifiers.
Now, we do need to test this behavior of "The CreateOrderViewController should do something on load with this request data" somehow, right? How do we test this if we can't invoke doSomethingOnLoad() because it is a private method? You call viewDidLoad(). The viewDidLoad() method is a boundary method. Which boundary? The boundary between the user and view controller! The user did something to the device to make it load another screen. So how do we invoke viewDidLoad() then? You do it like this:
let bundle = NSBundle(forClass: self.dynamicType)
let storyboard = UIStoryboard(name: "Main", bundle: bundle)
let createOrderViewController = storyboard.instantiateViewControllerWithIdentifier("CreateOrderViewController") as! CreateOrderViewController
let view = createOrderViewController.view
Simply calling the createOrderViewController.view property will cause viewDidLoad() to be invoked. I learned this trick a long time ago from someone. But Natasha The Robot also recently mentioned it too.
When we decide what to test, it is very important to only test the boundary methods. If we test every method of a class, the tests become extremely fragile. Every change we make to the code will break many, many tests. A lot of people give up because of this.
Or, think about it this way. When you ask how to mock CreateOrderRequest, first ask if doSomethingOnLoad() is a boundary method that you should write test for. If not, what is? The boundary method is actually viewDidLoad() in this case. The input is "when this view loads." The output is "call this method with this request object."
This is another benefit of using Clean Swift. All your boundary methods are listed at the top of the file under explicitly named protocols CreateOrderViewControllerInput and CreateOrderViewControllerOutput. You don't need to look elsewhere!
Think about what happens if you were to test doSomethingOnLoad(). You mock the request object, then assert that it equals to your expected request object. You are mocking something and comparing it. It's like assert(1, 1) instead of var a=1; assert(a, 1). What's the point? Too many tests. Too fragile.
Now, there is a time when you do mock CreateOrderRequest. After you've verified the correct CreateOrderRequest can be generated by the view controller component. When you test CreateOrderInteractor's doSomething() boundary method, you then mock CreateOrderRequest using interface dependency injection.
In short, unit testing is not about testing every unit of a class. It is about testing the class as a unit.
It is a mindset shift.
Hope that helps!
I have 3 series of draft posts in Wordpress on different topics:
In-depth look at each of the Clean Swift components
How to break up complex business logic into workers and service objects.
Writing tests in Clean Swift iOS architecture
Which one of these do you want to hear more first? Should I bump up the series on testing?
In my opinion Clean Architecture is a set of ideas, rules, principles... to make a code better.
[Android Clean Architecture]
With this approach I can use these targets in the way I did with android modules.
You are able create a target[About](application target or framework target...) but it depends on your needs
If you read Architecting Android...Reloaded from Fernando Cejas
you might have seen that I used android modules for representing each layer involved in the architecture.
A recurring question in discussions was: Why? The answer is simple… Wrong technical decision
The idea is that is not necessary to use some build components to implement Clean Architecture
Can Android acceptance tests be written using Robolectric? It seems to be classed only as a unit-testing framework.
Why can it not be classed as acceptance or "end-to-end" testing framework? (Can it be adapted for that purpose?)
I think this may be a bit subjective, but I feel it's answerable, so here's my take on it.
Is it an e2e testing framework?
It cannot be "end-to-end" because the application doesn't actually run on a device, the dependencies may be mocked out, network is usually mocked out and the tests don't simulate exactly how a user would work with the app. The lifecycle is simulated and controlled by you in the tests. It doesn't even run on the dalvik-VM and there are difference between dalvik and the JVM (here's a simple but relevant example: British Summer Time - BST not recognised by SimpleDateFormat timezone)
For me, an end-to-end test can only be achieved with a deployed application, using the network, connecting to the server it needs to, getting the information, showing it correctly on screen and the test verifying what can be seen on screen. Again, this may be subjective, but that's what e2e means to me.
Can it be used to write acceptance tests?
That depends on what you want from your acceptance tests, as I've seen people use this term differently, but I would say the answer is a definite yes. We use robolectric to test most of our business logic as we have found UI tests to not be reliable.
You can instantiate your UI elements with the right data, interact with UI elements, check what they show on screen. I would say this covers a lot of ground.
That said, sometimes these tests were very difficult to write, and we got a lot out of architecting our code differently (Model-View-Presenter is most commonly suggested for that)
The key for us was to have pretty "dumb" views and to let the presenter determine what will be shown on screen. Then you test that presenter heavily with robolectric tests and you can definitely write tests of the type "given this series of inputs, I expect to see these outputs on screen". Testing the view further adds little value because it's just a shell for what your presenter serves it.
I think describing how to apply MVP to your project is outside the scope of a SO answer, but I hope this helps.
Suppose you are tasked with adding a testing framework to an existing code base that has very very little unit testing coverage. The code base isn't insanely large yet, however, it does have areas where it's not super clean, or not very OOP or testable.
I've read a couple of good answers:
Adding unit tests to legacy code
How to approach unit testing in a large project
Best Option for Retrospective application of TDD into C# codebase
But the project I'm working on is an Android app, so it's slightly different (given lots more UI components).
I have some questions that all relate to the same issue:
What's the best way to do go back and put a bunch of tests in place?
How do I prioritize which parts to test first?
do I start with areas where it's getting called a lot (and is there a tool for code analysis like this)?
or do I go back and look at classes which in the past have had the most number of bugs?
Should I write integration tests first (given this is an Android app and also integration tests may prevent broken unit tests/refactorings) and then work in the unit tests?
Sorry for all the questions, just really looking for a good approach, more specifically geared towards retrospectively testing an Android app.
P.S. I'm curious if people know of good tools they can use for code analysis that can help bring to attention the areas in a code base in which unit testing would be most helpful.
Usually when starting with unit testing on an existing application you would want to do it iteratively - you probably do not have the time or the man power for a big upfront investment and so you want to add unit tests as part of the required work:
Write unt tests When adding a new feature - or better yet use TDD
When fixing a bug - write unit test(s) that fails de to the bug before fixing it
When refactoring old code wriute unit tests to make sure no regression bus were introduced
Id the whole team follow the three steps above in a matter of weeks you should have good coverage for the code you've been changing - depending on the size of the project.
I don't have an Android-specific answer. I hope someone comes along and adds a good one. Meanwhile: write acceptance (integration) tests for your most important features (from your product owner's point of view, not from some tool's). You'll get the most coverage for your effort, and be the most likely to prevent bugs that customers care about. Don't worry so much about unit tests; those are for details.
[That's the answer given in the third post you cite, but we're still waiting for that Android-savvy answer so let's not call this a duplicate yet.]
Do write acceptance tests and, as necessary, unit tests for new features, and write unit tests when fixing bugs.
Regarding how to find areas of the code that need tests, the first thing to do is measure code coverage. (You can do that in the Android SDK with ant emma debug install test.) Obviously code with no coverage needs tests.