Click here to close now.

Welcome!

Java Authors: Elizabeth White, Carmen Gonzalez, Pat Romanski, Kelly Murphy, AppDynamics Blog

Related Topics: Java, .NET, Open Source, AJAX & REA, Ruby

Java: Blog Post

Slow Tests Are the Symptom, Not the Cause

Test slowness is merely the symptom; what you should really address is the cause

If you have a slow test suite and you are asking yourself "how can I make my tests faster?" then you are asking the wrong question. Most chances are that you have bigger problems than just slow tests. The test slowness is merely the symptom; what you should really address is the cause. Once the real cause is addressed you will find that it's easy to write new fast tests and straightforward to refactor existing tests.

It's surprising how quickly a rails app's test suite can become slow. It's important to understand the reason for this slowness early on and address the real cause behind it. In most cases the reason is excessive coupling between the domain objects themselves and coupling between these objects and the framework.

In this refactoring walk-through we will see how small, incremental improvements to the design of a rails app, and specifically, decoupling, naturally lead to faster tests. We will extract service objects, completely remove all rails dependencies in test time and otherwise reduce the amount of coupling in the app.

Our goal is to have a simple, flexible and easy to maintain system in which objects can be replaced with other objects with minimal code changes. We will strive to achieve this goal and observe the effect of it on our tests speed.

Starting with a Fat Controller
Suppose we have a controller that's responsible for handling users signing up for a mailing list:

class MailingListsController < ApplicationController

respond_to :json

def add_user

user = User.find_by!(username: params[:username])

NotifiesUser.run(user, 'blog_list')

user.update_attributes(mailing_list_name: 'blog_list')

respond_with user

end

end

We first find the user (an exception is raised if the user is not found). Then we notify the user she was added to the mailing list via NotifiesUser (probably asking her to confirm). We update the user record with the name of the mailing list and then hand the user object to respond_with, which will render the json representation of the user or the proper error response in case saving of the object failed.

The logic here is pretty straight-forward, but it's still too complicated for a controller and should be extracted out. But where to? The word user in every line in this method suggests that we should push it into the User model (that's called Feature Envy). Let's try this:

Extracting Logic to a Fat Model

class MailingListsController < ApplicationController

respond_to :json

def add_user

user = User.add_to_mailing_list(params[:username], 'blog_list')

respond_with user

end

end

class User < ActiveRecord::Base

validates_uniqueness_of :username

def self.add_to_mailing_list(username, mailing_list_name)

user = User.find_by!(username: username)

NotifiesUser.run(user, 'blog_list')

user.update_attributes(mailing_list_name: 'blog_list')

user

end

end

This is better: the User class is now responsible for creating and updating users. But there is a problem: now User is handling mailing list additions, as well as user notifications. These are too many responsibilities for one class. Having an active record object handle anything more than CRUD, associations and validations is a (further) violation of the Single Responsibility Principle.

The result is that business logic in active record classes is a pain to unit test. You often need to use factories or to heavily stub out methods of the object under test (don't do that), stub all instances of the class under test (don't do that either) or hit the database in your unit tests (please don't). As a result, testing active record objects can be very slow, sometimes orders of magnitude slower than testing plain ruby objects.

Now, if the code above was the entire User class and my application was small and simple I might have been happy with leaving User#add_to_mailing_list as is. But in a bit bigger rails apps that are not groomed often enough, models, controllers and domain logic tend to get tangled (coupled) together and needlessly complicate things (Rich Hickey, the inventor of clojure, calls it incidental complexity). This is when introducing a service objectis helpful:

Extracting a Service Object

class MailingListsController < ApplicationController
respond_to :json
def add_user
user = AddsUserToList.run(params[:username], 'blog_list')
respond_with user
end
end
class AddsUserToList
def self.run(username, mailing_list_name)
user = User.find_by!(username: username)
NotifiesUser.run(user, 'blog_list')
user.update_attributes(mailing_list_name: 'blog_list')
user
end
end

We created a plain ruby object, AddsUserToList, which contains the business logic from before. In the controller we call this object and not User directly. This is an improvement, but hard-coding the name of the class of your collaborator is a bad idea since it couples the two together and makes it impossible to replace the class with a different implementation. Not surprisingly, the result of this coupling is that testing becomes harder and tests slower. Testing this service object would require us to somehow stub User#find_by! to avoid hitting the database, and probably also stub out NotifiesUser#run in order to avoid sending a real notification out.

Also, referencing the class User directly means that our unit tests will have to load active record and the entire rails stack, but even worse - the entire app and its dependencies. This load time can be a few seconds for trivial rails apps, but can sometimes be 30 seconds for bigger apps. Unit tests should be fast to run as part of your test suite but also fast to run individually, which means they should not load the rails stack or your application (also seeCorey Haines's talk on the subject).

The most straight forward way to decouple the object from its collaborators is to inject the dependencies of AddsUserToList:

Injecting Dependencies

class AddsUserToList
def self.run(username, mailing_list_name, finds_user = User, notifies_user = NotifiesUser)
finds_user.find_by!(username: username)
notifies_user.(user, mailing_list_name)
user.update_attributes(mailing_list_name: mailing_list_name)
user
end
end

We can now pass as an argument any class that finds a user and any class that notifies a user, which means that passing different implementations will be easy. It also means that testing will be easier. Since we supplied reasonable defaults we don't need to be explicit about these dependencies if we don't change them, and our controller can stay unchanged.

The fact that we are specifying User as the default value of finds_user in the parameter list does not mean that this class and all its dependents (ActiveRecord, our app and other gems) will get loaded. Ruby's Deferred Evaluation of the default values means that if these default values are not needed they will not get loaded, so we can run this unit test without loading rails.

Simplifying the Interface
The method AddsUserToList#run receives 4 arguments. Users of this method need to know the order of the list. Also, it is likely that over time you'd discover you need to add more arguments. When this happens you will need to update all users of the method. A more flexible solution is to use a hash of arguments. This will make the interface more stable and ensure the number of arguments does not grow when we find that we need to add more arguments. It will also make refactoring a little easier, which is important. I often find that for many classes I end up changing from an argument list to a hash of arguments at some point, so why not use it in the first place? But does it mean that we need to give up the advantages of deferred evaluation of the default values? Not at all.

We will use Hash#fetch, passing a block to it, which will not get evaluated unless the queried key is absent. In our tests, the code in the block to fetch will never get evaluated, and User won't get loaded. Also, when specifying the defaults in the argument list it is not possible to evaluate more than one statement, but we can do it using Hash#fetch.

One more thing: when my classes contain only one public method I don't like calling it rundo or perform since these names don't convey a lot of information. In this case I'd rather call it call and use ruby's shorthand notation for invoking this method. This also enables me to pass in a proc instead of the class itself if I need it.

class AddsUserToList
def self.run(args)
finds_user = args.fetch(:finds_user) { User }
notifies_user = args.fetch(:notifies_user) { NotifiesUser }
finds_user.find_by!(username: args.fetch(:username))
notifies_user.(user, args.fetch(:mailing_list_name))
user.update_attributes(mailing_list_name: args.fetch(:mailing_list_name))
user
end
end

Using Ruby 2.1's Keyword Arguments Syntax

We can get the same exact functionality by using ruby's 2.1's keyword argument syntax. See how much less verbose this version is:

class AddsUserToList
def self.call(username:, mailing_list_name:, finds_user: User,
notifies_user: NotifiesUser)
user = finds_user.find_by_username!(username)
notifies_user.(user, mailing_list_name)
user.add_to_mailing_list(mailing_list_name)
user
end
end

More Stories By Manuel Weiss

I am the cofounder of Codeship – a hosted Continuous Integration and Deployment platform for web applications. On the Codeship blog we love to write about Software Testing, Continuos Integration and Deployment. Also check out our weekly screencast series 'Testing Tuesday'!

@ThingsExpo Stories
One of the biggest challenges when developing connected devices is identifying user value and delivering it through successful user experiences. In his session at Internet of @ThingsExpo, Mike Kuniavsky, Principal Scientist, Innovation Services at PARC, described an IoT-specific approach to user experience design that combines approaches from interaction design, industrial design and service design to create experiences that go beyond simple connected gadgets to create lasting, multi-device experiences grounded in people's real needs and desires.
The 4th International Internet of @ThingsExpo, co-located with the 17th International Cloud Expo - to be held November 3-5, 2015, at the Santa Clara Convention Center in Santa Clara, CA - announces that its Call for Papers is open. The Internet of Things (IoT) is the biggest idea since the creation of the Worldwide Web more than 20 years ago.
Wearable devices have come of age. The primary applications of wearables so far have been "the Quantified Self" or the tracking of one's fitness and health status. We propose the evolution of wearables into social and emotional communication devices. Our BE(tm) sensor uses light to visualize the skin conductance response. Our sensors are very inexpensive and can be massively distributed to audiences or groups of any size, in order to gauge reactions to performances, video, or any kind of presentation. In her session at @ThingsExpo, Jocelyn Scheirer, CEO & Founder of Bionolux, will discuss ho...
The true value of the Internet of Things (IoT) lies not just in the data, but through the services that protect the data, perform the analysis and present findings in a usable way. With many IoT elements rooted in traditional IT components, Big Data and IoT isn’t just a play for enterprise. In fact, the IoT presents SMBs with the prospect of launching entirely new activities and exploring innovative areas. CompTIA research identifies several areas where IoT is expected to have the greatest impact.
Can call centers hang up the phones for good? Intuitive Solutions did. WebRTC enabled this contact center provider to eliminate antiquated telephony and desktop phone infrastructure with a pure web-based solution, allowing them to expand beyond brick-and-mortar confines to a home-based agent model. It also ensured scalability and better service for customers, including MUY! Companies, one of the country's largest franchise restaurant companies with 232 Pizza Hut locations. This is one example of WebRTC adoption today, but the potential is limitless when powered by IoT.
Every day we read jaw-dropping stats on the explosion of data. We allocate significant resources to harness and better understand it. We build businesses around it. But we’ve only just begun. For big payoffs in Big Data, CIOs are turning to cognitive computing. Cognitive computing’s ability to securely extract insights, understand natural language, and get smarter each time it’s used is the next, logical step for Big Data.
The Internet of Things will greatly expand the opportunities for data collection and new business models driven off of that data. In her session at @ThingsExpo, Esmeralda Swartz, CMO of MetraTech, discussed how for this to be effective you not only need to have infrastructure and operational models capable of utilizing this new phenomenon, but increasingly service providers will need to convince a skeptical public to participate. Get ready to show them the money!
SYS-CON Events announced today that MetraTech, now part of Ericsson, has been named “Silver Sponsor” of SYS-CON's 16th International Cloud Expo®, which will take place on June 9–11, 2015, at the Javits Center in New York, NY. Ericsson is the driving force behind the Networked Society- a world leader in communications infrastructure, software and services. Some 40% of the world’s mobile traffic runs through networks Ericsson has supplied, serving more than 2.5 billion subscribers.
The 17th International Cloud Expo has announced that its Call for Papers is open. 17th International Cloud Expo, to be held November 3-5, 2015, at the Santa Clara Convention Center in Santa Clara, CA, brings together Cloud Computing, APM, APIs, Microservices, Security, Big Data, Internet of Things, DevOps and WebRTC to one location. With cloud computing driving a higher percentage of enterprise IT budgets every year, it becomes increasingly important to plant your flag in this fast-expanding business opportunity. Submit your speaking proposal today!
17th Cloud Expo, taking place Nov 3-5, 2015, at the Santa Clara Convention Center in Santa Clara, CA, will feature technical sessions from a rock star conference faculty and the leading industry players in the world. Cloud computing is now being embraced by a majority of enterprises of all sizes. Yesterday's debate about public vs. private has transformed into the reality of hybrid cloud: a recent survey shows that 74% of enterprises have a hybrid cloud strategy. Meanwhile, 94% of enterprises are using some form of XaaS – software, platform, and infrastructure as a service.
The Internet of Things is not new. Historically, smart businesses have used its basic concept of leveraging data to drive better decision making and have capitalized on those insights to realize additional revenue opportunities. So, what has changed to make the Internet of Things one of the hottest topics in tech? In his session at @ThingsExpo, Chris Gray, Director, Embedded and Internet of Things, discussed the underlying factors that are driving the economics of intelligent systems. Discover how hardware commoditization, the ubiquitous nature of connectivity, and the emergence of Big Data a...
With major technology companies and startups seriously embracing IoT strategies, now is the perfect time to attend @ThingsExpo in Silicon Valley. Learn what is going on, contribute to the discussions, and ensure that your enterprise is as "IoT-Ready" as it can be! Internet of @ThingsExpo, taking place Nov 3-5, 2015, at the Santa Clara Convention Center in Santa Clara, CA, is co-located with 17th Cloud Expo and will feature technical sessions from a rock star conference faculty and the leading industry players in the world. The Internet of Things (IoT) is the most profound change in personal an...
All major researchers estimate there will be tens of billions devices - computers, smartphones, tablets, and sensors - connected to the Internet by 2020. This number will continue to grow at a rapid pace for the next several decades. With major technology companies and startups seriously embracing IoT strategies, now is the perfect time to attend @ThingsExpo, June 9-11, 2015, at the Javits Center in New York City. Learn what is going on, contribute to the discussions, and ensure that your enterprise is as "IoT-Ready" as it can be
SYS-CON Events announced today that DragonGlass, an enterprise search platform, will exhibit at SYS-CON's 16th International Cloud Expo®, which will take place on June 9-11, 2015, at the Javits Center in New York City, NY. After eleven years of designing and building custom applications, OpenCrowd has launched DragonGlass, a cloud-based platform that enables the development of search-based applications. These are a new breed of applications that utilize a search index as their backbone for data retrieval. They can easily adapt to new data sets and provide access to both structured and unstruc...
Container frameworks, such as Docker, provide a variety of benefits, including density of deployment across infrastructure, convenience for application developers to push updates with low operational hand-holding, and a fairly well-defined deployment workflow that can be orchestrated. Container frameworks also enable a DevOps approach to application development by cleanly separating concerns between operations and development teams. But running multi-container, multi-server apps with containers is very hard. You have to learn five new and different technologies and best practices (libswarm, sy...
As the Internet of Things unfolds, mobile and wearable devices are blurring the line between physical and digital, integrating ever more closely with our interests, our routines, our daily lives. Contextual computing and smart, sensor-equipped spaces bring the potential to walk through a world that recognizes us and responds accordingly. We become continuous transmitters and receivers of data. In his session at @ThingsExpo, Andrew Bolwell, Director of Innovation for HP's Printing and Personal Systems Group, discussed how key attributes of mobile technology – touch input, sensors, social, and ...
WebRTC defines no default signaling protocol, causing fragmentation between WebRTC silos. SIP and XMPP provide possibilities, but come with considerable complexity and are not designed for use in a web environment. In his session at @ThingsExpo, Matthew Hodgson, technical co-founder of the Matrix.org, discussed how Matrix is a new non-profit Open Source Project that defines both a new HTTP-based standard for VoIP & IM signaling and provides reference implementations.
SYS-CON Events announced today that the "First Containers & Microservices Conference" will take place June 9-11, 2015, at the Javits Center in New York City. The “Second Containers & Microservices Conference” will take place November 3-5, 2015, at Santa Clara Convention Center, Santa Clara, CA. Containers and microservices have become topics of intense interest throughout the cloud developer and enterprise IT communities.
Buzzword alert: Microservices and IoT at a DevOps conference? What could possibly go wrong? In this Power Panel at DevOps Summit, moderated by Jason Bloomberg, the leading expert on architecting agility for the enterprise and president of Intellyx, panelists will peel away the buzz and discuss the important architectural principles behind implementing IoT solutions for the enterprise. As remote IoT devices and sensors become increasingly intelligent, they become part of our distributed cloud environment, and we must architect and code accordingly. At the very least, you'll have no problem fil...
Almost everyone sees the potential of Internet of Things but how can businesses truly unlock that potential. The key will be in the ability to discover business insight in the midst of an ocean of Big Data generated from billions of embedded devices via Systems of Discover. Businesses will also need to ensure that they can sustain that insight by leveraging the cloud for global reach, scale and elasticity.