Do Wall Decals Damage Paint?

There is no straight-forward answer to this question as it depends upon several factors to include what type of adhesive is applied to the decals or stickers you are purchasing, the type of…

Smartphone

独家优惠奖金 100% 高达 1 BTC + 180 免费旋转




Breaking Dependencies

Dependencies are one of the biggest challenges to getting legacy code into a test harness. Especially dependencies on the Android framework.

In the Android world, having a dependency on an activity, fragment, or view is basically a non-starter for unit testing. Other types of dependencies that pose challenges to unit testing include networking calls, file I/O, database access, and globals, just to name a few.

Working Effectively with Legacy Code, Michael Feathers (2005)

In the previous post, we extracted a simple presenter class to allow us to unit test the presentation logic that was originally inline in a button click handler. The key here was breaking the dependency on the Android framework to allow us to unit test the logic in isolation.

Last time we were able to first add a few instrumentation tests as a bit of a safety net before refactoring. Unfortunately, is not always possible. Let’s look at another example, this time in Java so we can enjoy a more authentic legacy code experience.

UserPresenter.java

In this example, we have an app with a feature to display a user profile screen.

The good news is we already have a UserPresenter class decoupled from the Activity.

The bad news is the presenter depends on a DatabaseManager and a Context.

Even worse, the DatabaseManager is a singleton. Having a dependency on a global singleton can be particularly challenging when trying to get a class into a unit test harness.

Let’s also assume we can’t introduce instrumentation tests for this feature without pre-populating the database with user profile data and navigating a complex series of UI interactions. While this might sound interesting to you, I can think of better ways to spend my time. Remember we would have to do this every time we run the tests on an emulator or device.

And after all that, you still won’t have the ability to write unit tests that can verify the behavior of your presenter in isolation. As an alternative, we can use a dependency breaking technique.

Dependency breaking techniques are minimally invasive refactors that can help make a system more testable.

Isn’t there risk when you start refactoring without having tests in place? Of course. However these techniques are designed so the chance of making a mistake is very small.

Breaking dependencies in legacy code will allow the introduction of tests into the system before making more invasive design improvements.

In the above example, we actually have a few different options for how to break the dependency of the UserPresenter on the DatabaseManager singleton and the Android Context.

Rather than relying on the getInstance() method of the DatabaseManager singleton, we could pass an instance of the class into the constructor.

This would allow us to inject a fake or mock DatabaseManager in the test environment into the UserPresenter.

UserPresenter.java

We can perform a similar technique by introducing a new parameter to the onProfileButtonClick method.

UserProfile.java

A third way we can decouple the dependency on the real DatabaseManager in tests is by introducing a static setter method that allows us to replace the real instance by a fake instance in the test environment.

Let’s say our DatabaseManager looks something like the following. For the sake of brevity we’ll omit the details of the database access.

DatabaseManager.java

Introducing a static setter method that can be used in the test harness requires a few small steps.

DatabaseManager.java

Does this looks a little gross? Yes. When introducing tests in legacy code, sometimes the design of the program will get worse before it gets better.

Once we have tests in place, we can start to make more comprehensive design improvements. For example, perhaps we could eventually migrate the DatabaseManager from a singleton to a normal instance class.

Which one of these dependency breaking technique is best to use? This answer is individual to your program.

Parameterize Constructor and Parameterize Method are less invasive, but can lead to widespread changes in the code base if many classes have the same dependency.

Introduce Static Setter is a slightly more invasive technique, as it introduces “test only” code into the production class. Yet this can be a reasonable trade-off when there are many classes that depend on the same singleton instance.

This post is part of a series on working with legacy code on Android. It explores ways we can navigate, maintain, improve, and evolve legacy code using clean architecture, refactoring, dependency breaking techniques, and testing.

If you found this article helpful, please give it some applause 👏 to help others find it. You can also leave a comment below. Thanks!

Add a comment

Related posts:

Learning Beyond Limited Labels

As most machine learning practitioners know, the primary challenge for machine learning as a field continues to be ensuring that trained learning systems are capable of generalizing beyond limited…

Como una manzanita

Te ofrecemos esta vez varios consejos que vienen muy bien a la hora de mantener una apariencia saludable. “Sana como una manzana”, acostumbraban a decir los mayores al referirse a cualquier persona…

6 Quick Negotiation Tenets You Need To Know To Gain The Upper Hand In Any Situation

Maybe you want to negotiate a salary. Maybe you’re having a debate with a friend. Maybe you want a specific project to move in a different direction. Maybe you’re trying to say no to something. Or…