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

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

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

Related

  • The Most Popular Technologies for Java Microservices Right Now
  • Test-Driven Development With The oclif Testing Library: Part One
  • Micronaut With Relation Database and...Tests
  • Mastering Unit Testing and Test-Driven Development in Java

Trending

  • Modern Test Automation With AI (LLM) and Playwright MCP
  • How to Convert XLS to XLSX in Java
  • Monolith: The Good, The Bad and The Ugly
  • The Role of AI in Identity and Access Management for Organizations
  1. DZone
  2. Testing, Deployment, and Maintenance
  3. Testing, Tools, and Frameworks
  4. Dependency Injection with Test Driven Development

Dependency Injection with Test Driven Development

By 
Paul Underwood user avatar
Paul Underwood
·
Mar. 14, 13 · Interview
Likes (2)
Comment
Save
Tweet
Share
8.8K Views

Join the DZone community and get the full member experience.

Join For Free

With unit tests you can check that your code behaviours just as you expect it to. When writing your unit tests you shouldn't need to worry about if any other area of the application is working correctly.

The benefits of unit testing are:

  • Decouples your code
  • Write more modular classes
  • Functions are smaller and more focused
  • Your functions are more defensive
  • Quality of code becomes higher
  • You will find it easier to reuse code.

When writing unit tests you just need to test this one method of your application, if your method relies on another class/variable there should be a way you can inject this into the method. This is where dependency injection in your code comes in handy, it will allow you to inject objects into your classes to change the output of the class.

There are a few things you need to do to make a method unit testable, methods will need an input from a parameter or a class variable and it will need a return or set a class variable in the method. If the method hasn't got these things then the method can not be unit testable. If there isn't a return of the method then there is no way in knowing how the method performs.

Dependency Injection

Dependency injection is when your object has a dependency on another object. The simplest form to understand what dependency injection is to think of a setter method. A setter method will take one parameter and set a class variable from this parameter. This is using code injection to pass in a parameter to be used as the class variable value.

public function setValue( $val )
{
      $this->val = $val;
}

Without dependency injection this method will look like this.

public function setValue()
{
      $this->val = 10;
}

For unit testing you need to be aware of any classes that your class is dependent on. For example if you have a login class that will connect to a database.

class login
{
     private $db = false;
     public function __construct()
     {
          $this->db = new Database();
     }
     public function loginUser( $user, $password )
     {
           $this->db->checkLogin( $user, $password );
     }
}

This login class has a dependency of the class Database in the constructor, which means that we can't unit test this correctly. If we want to unit test this then the database class has to be development and tested. If the database class is broken and we try to unit test the loginUser() method the test will always fail and we won't know that it's the database class which is broke or the loginUser() method that is broke.

If the database class is finished development, tested and data is in the database then we can use this for the loginUser() function. But now our tests are dependent on data being correct in the database. If we pass in a username and password it must be in the database for our test to pass. Our code could be correct but if the data isn't there then our unit tests will fail. This isn't correct use of unit tests and is more suited to be an integration test.

To fix this problem we can use dependency injection to pass in a database connector which will set the database class variable. There are 2 ways we can inject a variable into a class, it can either be in the constructor of the class or by using a setter method. I tend to use constructor for all required dependences and use the setter method if there is a default value for the class variable.

class login
{
     private $db = false;
     public function __construct( $db )
     {
          $this->db = $db;
     }
     public function loginUser( $user, $password )
     {
          $this->db->checkLogin( $user, $password );
     }
}

Now this class isn't dependant on a certain database class we can pass in the database class by using the parameter on the login class constructor.

We can unit test this loginUser() method by first setting the $this->db class variable. We don't want to rely on a real database as the data can change so we can either create a test harness database class or you can mock the database class.

A test harness class will allow you to create your database class and hardcode any data that you need. In the example above we can create a method checkLogin(), in our test harness we can then hardcode a successful login username and password to make the loginUser() method pass. Or you can use a PHP mocking framework to mock a class/method/return value.

Both methods have their benefits but mocking is normally quicker to code, but there are times when you want to hardcode certain variables in a class.

Mocking Objects In TDD With PHP

Mocking objects in test driven development allows you create objects to act as a certain class, if your test depends on another method to return a value, you can mock this method and make it return any value you want.

In the example we used above you can mock the database class and choose what value we are expecting back from the checkLogin() method. When mocking a method you can choose what you want to return from this method, therefore we can write tests to see what will happen when checkLogin() returns TRUE and then we can write another test to see what happens when checkLogin() returns FALSE.

Mocking objects means that you can run your unit tests without depending on another class returning the values you are expecting, ao you can test just your code in this one method.

Here are some of the most popular PHP mocking frameworks:

  • Mocking with PHPUnit - http://www.phpunit.de/manual/3.0/en/mock-objects.html
  • Mocking with Phake - http://phake.digitalsandwich.com/docs/html/
  • Mocking with Mockery - https://github.com/padraic/mockery
  • Mocking with Enchane PHP - https://github.com/Enhance-PHP/Enhance-PHP
  • Mocking with FBMock - https://github.com/facebook/FBMock

Dependency Injection With Interfaces

If we are going to pass in a database connector in a constructor of the login class, then this database connector will always have to have a method of checkLogin(). This is why we should code our dependences by using interfaces to make sure that we are always passing in the correct type of class.

class login
{
     private $db = false;
     public function __construct( IDatabase $db )
     {
          $this->db = $db;
     }
}
class database implements IDatabase
{
     public function checkLogin( $username, $password )
     {
           // check the login credentials
     }
}
interface IDatabase
{
     public function checkLogin( $username, $password );
}

This will make sure that the class we pass into the constructor is a type of IDatabase, so if our database class doesn't implement IDatabase then the code will fail and therefore our unit tests will fail. This means whatever we pass into the constructor we know that this class will be able to run the methods it needs for the unit tests to run.

unit test Dependency injection Database Test-driven development

Published at DZone with permission of Paul Underwood, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • The Most Popular Technologies for Java Microservices Right Now
  • Test-Driven Development With The oclif Testing Library: Part One
  • Micronaut With Relation Database and...Tests
  • Mastering Unit Testing and Test-Driven Development in Java

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!