DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Last call! Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Understanding the Two Schools of Unit Testing
  • Maven Dependency Scope Applied
  • Testing the Untestable and Other Anti-Patterns
  • Refactor Switch to a One-Liner

Trending

  • Automatic Code Transformation With OpenRewrite
  • Automating Data Pipelines: Generating PySpark and SQL Jobs With LLMs in Cloudera
  • 5 Subtle Indicators Your Development Environment Is Under Siege
  • Testing SingleStore's MCP Server
  1. DZone
  2. Software Design and Architecture
  3. Security
  4. Unit Testing 101: Inversion Of Control

Unit Testing 101: Inversion Of Control

By 
Luis Aguilar user avatar
Luis Aguilar
·
Apr. 19, 13 · Interview
Likes (0)
Comment
Save
Tweet
Share
16.1K Views

Join the DZone community and get the full member experience.

Join For Free

inversion of control is one of the most common and widely used techniques for handling class dependencies in software development and could easily be the most important practice in unit testing. basically, it determines if your code is unit-testable or not. not just that, but it can also help improve significantly your overall software structure and design. but what is it all about? it is really that important? hopefully we’ll clear those out on the following lines.

identifying class dependencies

as we mentioned before,  inversion of control is a technique used to handle class dependencies effectively; but, what exactly is a dependency ? in real life, for instance, a car needs an engine in order to function; without it, it probably won’t work at all. in programming it is the same thing; when a class needs another one in order to function properly, it has a dependency on it. this is called a class dependency or coupling .

let’s look at the following code example:

public class usermanager
{
    private md5passwordhasher passwordhasher;

    public usermanager()
    {
        this.passwordhasher = new md5passwordhasher();
    }

    public void resetpassword(string username, string password)
    {
        // get the user from the database
        user user = datacontext.users.getbyname(username);

        string hashedpassword = this.passwordhasher.hash(password);

        // set the user new password
        user.password = hashedpassword;

        // save the user back to the database.
        datacontext.users.update(user);
        datacontext.commit();
    }

    // more methods...
}

public class md5passwordhasher
{
    public string hash(string plaintextpassword)
    {
        // hash password using an encryption algorithm...
    }
}

the previous code describes two classes, usermanager and passwordhasher . we can see how usermanager class initializes a new instance of the passwordhasher class on its constructor and keeps it as a class-level variable so all methods in the class can use it (line 3). the method we are going to focus on is the resetpassword method. as you might have already noticed, the line 15 is highlighted. this line makes use of the passwordhasher instance, hence, marking a strong class dependency between usermanager and passwordhasher .

don’t call us, we’ll call you

when a class creates instances of its dependencies, it knows what implementation of that dependency is using and probably how it works. the class is the one controlling its own behavior. by using inversion of control, anyone using that class can specify the concrete implementation of each of the dependencies used by it; this time the class user is the one partially controlling the class behavior (or how it behaves on the parts where it uses those provided dependencies).

anyways, all of this is quite confusing. let’s look at an example:

public class usermanager
{
    private ipasswordhasher passwordhasher;

    public usermanager(ipasswordhasher passwordhasher)
    {
        this.passwordhasher = passwordhasher;
    }

    public void resetpassword(string username, string password)
    {
        // get the user from the database
        user user = datacontext.users.getbyname(username);

        string hashedpassword = this.passwordhasher.hash(password);

        // set the user new password
        user.password = hashedpassword;

        // save the user back to the database.
        datacontext.users.update(user);
        datacontext.commit();
    }

    // more methods...
}

public interface ipasswordhasher
{
    string hash(string plaintextpassword);
}

public class md5passwordhasher : ipasswordhasher
{
    public string hash(string plaintextpassword)
    {
        // hash password using an encryption algorithm...
    }
}

inversion of control is usually implemented by applying a design pattern called the strategy pattern (as defined in the gang of four book). this pattern consists on abstracting concrete component and algorithm implementations from the rest of the classes by exposing only an interface they can use; thus making implementations interchangeable at runtime and encapsulate how these implementations work since any class using them should not care about how they work.

the strategy pattern

so, in order to achieve this, we need to sort some things out:

  • abstract an interface from the md5passwordhasher class, ipasswordhasher ; so anyone can write custom implementations of password hashers (line 28-31).
  • mark the md5passwordhasher class as an implementation of the ipasswordhasher interface (line 33).
  • change the type of the password hasher used by usermanager to ipasswordhasher (line 3).
  • add a new constructor parameter of type ipasswordhasher interface (line 5), which is the instance the usermanager class will use to hash its passwords. this way we delegate the creation of dependencies to the user of the class and allows the user to provide any implementation it wants, allowing it to control how the password is going to be hashed.

this is the very essence of inversion of control: minimize class coupling. the user of the usermanager class has now control over how passwords are hashed. password hashing control has been inverted from the class to the user. here is an example on how we can specify the only dependency of the usermanager class:

ipasswordhasher md5passwordhasher = new md5passwordhasher();
usermanager usermanager = new usermanager(md5passwordhasher);

usermanager.resetpassword("luis.aguilar", "12345");

so, why is this useful? well, we can go crazy and create our own hasher implementation to be used by the usermanager class:

// plain text password hasher:
public class plaintextpasswordhasher : ipasswordhasher
{
    public string hash(string plaintextpassword)
    {
        // let's disable password hashing by returning
        // the plain text password.
        return plaintextpassword;
    }
}

// usage:
ipasswordhasher plaintextpasswordhasher = new plaintextpasswordhasher();
usermanager usermanager = new usermanager(plaintextpasswordhasher);

// resulting password will be: 12345.
usermanager.resetpassword("luis.aguilar", "12345");

conclusion

so, this concludes our article on inversion of control. hopefully with a little more practice, you will be able to start applying this to your code. of course, the  biggest benefit of this technique is related to unit testing. so, what does it has to do with unit testing? well, we’re going to see this when we get into type mocking . so, stay tuned!

Inversion of control unit test Dependency Implementation

Published at DZone with permission of Luis Aguilar, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Understanding the Two Schools of Unit Testing
  • Maven Dependency Scope Applied
  • Testing the Untestable and Other Anti-Patterns
  • Refactor Switch to a One-Liner

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!