What is Test Driven Development ?
TDD is a coding strategy based onwriting automated tests to recognize when and where to introduce or adjust code. TDD promotes cyclical repetition in order to make the process simpler. The development cycles are incredibly brief. The programmer starts off by creating a test case for a distinct wanted capability that is automated. The trial run inevitably did not work, so the programmer amended the failure and then tried again. The coding process keeps going until the output matches what is expected, and then the programmer proceeds to the next task. TDD ensures that each component of the code is functioning correctly, before dedicating efforts towards developing the entire application.
Sounds great, right?
As previously said, it might not be the most time-effective to test each piece of code multiple times in the beginning, especially when deadlines are approaching. Though it may be challenging to become knowledgeable about testing tools and procedures, the rewards ultimately make the effort worth it.
Benefits of Test Driven Development
Those who have experience with agile approach to IT will not be surprised by test-driven development. An agile practice focuses on the principles of iteration, feedback, flexibility, and solving individual issues prior to progressing. This allows for continuous betterment of the product throughout its various development stages. Just as nimble IT provides efficiency, it also accelerates the completion time.
Haven’t we just said that TDD takes a lot of time? Is it possible for a programming methodology to be both prohibitively slow and faster than other programming methodologies?
Well, it all comes down to perspective. Testing Driven Development (TDD) can appear to be time-consuming, as it necessitates testing, adjusting, and re-testing until the code is up to standard. Once the functionality has been checked and proven to be successful, there will be no need to revisit it. Once you become accustomed to the steps, it goes by fairly quickly. You will dedicating more time initially, but will save time in the long run; ultimately leading to a quicker completion. Due to the individual nature of each code, modifications and alterations can be carried out with no anxiousness of the system malfunctioning.
The finished product is a versatile code construct with separate parts that can be included, eliminated, and improved without the need to start over with the development process.
TDD can also help to give a much more complete knowledge of both the code and what the expected results should be. Test-driven development encourages coders to scrutinise individual problems rather than relying solely on a broad perspective. TDD necessitates that you tackle individual issues before continuing, thus making unit tests act like comprehensive code records as well as scenarios illustrating how the code is intended to work.
One of the most noteworthy advantages of test driven development is that it produces code that is dependable and as straightforward as it can be. This is because new code is only added as necessary in relation to each test result.
OK. So TDD is a viable and advantageous programming practise. So how is it done?
The Rules of TDD
This is a guide for newbies, so let’s keep it simple. But essentially, when you approach a TDD project, there are a few rules you need to keep in mind:
- Don’t write production code unless it’s to help a failing unit test pass.
- Only write enough of a unit test to fail.
- Only write enough production code to help a failing unit test pass.
Put simply, only write code when it is absolutely necessary. Your responsibility when working with TDD is to produce prompt feedback cycles. This suggests starting with the basics and testing just a little bit of code, and then change the code in order to make it pass the test.
Those are the bearbones of test driven development. The process is somewhat more complicated, and likely requires additional explanation. So, let’s talk about TDD refactor cycles.
The CycleThree-Step
TDD operates using a continual sequence of three parts: Creating a failing test, making it pass, then improving the code. You can think of these steps as follows:
- Red — What do you want the code to do?
- Green — How can you make the code do it?
- Refactor — How can you improve the code?
Once more, to repeat, the essential point here is that you are not looking at the whole situation; you are addressing the most defined and precise issues. Rather than inquiring “Does my app deliver what it is designed for?”, you will be asking “Can this particular, small detail of my app make the intended outcome?” As an example, we are not referencing if you can use the Uber app to unveil a driver; we are speaking about if the application has the capability to give a special notification when the driver gets there. Focus on resolving minor issues and gradually move up in difficulty.
Red Phase
When dealing with a problem that requires Test Driven Development, the first step is always to begin with the red phase. You should aim to compose a test during the red phase which will provide information concerning the functional capability of the feature.
Chances are, it does not work right at first. That’s to be expected, and it’s just fine. Discovering what you are wanting the feature to do, and observing where your code is not functioning as expected, will give you the opportunity to figure out which kind of remedial strategy may be necessary.
Having a clear understanding of the red test failure, it is now possible to progress to the green phase.
Green Phase
The green phase is like a ‘green light’ giving you permission to start attempting to resolve the problem. Don’t be daunted by needing to repair all problems immediately; just concentrate on solving the present dilemma – how can you have your test pass? That’s the only issue you must consider at the moment.
Do whatever is necessary to ensure its success.
We mean that. There is no need to stress about adhering to the ideal methods or ensuring everything is tidy. Do whatever is necessary to get the job done, even if it means writing redundant code, because achieving the desired outcome is most important. Clean up comes later.
Once you’ve solved the issue and gotten a positive result in your testing, it is time to begin the refactoring process.
Refactor Phase
Revising is the process of tidying up and improving upon the work you have done before while keeping all of your tests passing. Honestly, it is up to you to decide how much you want to improve and refine your work, as long as you get rid of any repeated code, make the solutions as simple as possible, and make the quality of your work more professional. Once you finish, start the process again by going to the next feature.
Once you’ve addressed every feature of your application, congratulations! If you have employed TDD properly, your application should be robust with no glitches and composed of adaptable divisions for superior suppleness. If you’ve gained the necessary experience with TDD, you could end up saving yourself some time.
Testing is at the heart of the development cycle – no refactoring of code is allowed unless there are tests present to verify it.
At first, it can take some time and effort – particularly if you’re accustomed to quickly producing coding. However, with time and dedication the returns are astounding. For example:
- Because you’re only writing just enough code to pass a test, there’s less room for bugs to hide.
- Having a solid suite of tests provides the foundation for a CI/CD (Continuous Integration/Continuous Development) pipeline.
- Test cases can be used to automatically generate documentation.
- That fear of the unknown is gone which means you can finally unleash that code ninja inside you.
In this piece, I will start a chain of articles which will demonstrate the process of creating an API for a Peer-to-peer payment program utilizing Test-Driven Development, Codeception, and Symfony. It will have some restrictions, but it will give you lots of chances to be introduced to TDD and the three-phase cycle of Red-Green-Refactor.
Codeception is a PHP testing framework that improves the testing experience by building on PHPUnit. I decided to go with Codeception over other systems due to its distinct nature which makes Test-Driven-Development less daunting to adopt. You can concisely outline what you want to achieve before focusing on how to accomplish it.
In addition to constructing a Symfony application and establishing Codeception for testing, we’ll create the authorization capacity for the API and analyze several TDD ideas.
Prerequisites
To get the most out of this tutorial, you need the following:
Create the base Symfony application
To begin, generate a fresh Symfony project titled codeception-tdd, and enter the directory by executing the following commands.
symfony new codeception-tdd
cd codeception-tdd
Up next, in order to utilize PHP 7.4 or greater capabilities, open the composer.json document and verify that the require section requests the PHP to be 7.4 or higher, in alignment with the example stated below.
Install the required dependencies
After that, install the project’s dependencies. For this project we will use:
- Doctrine : To help with managing the application’s database.
- Faker : To generate fake data for our application.
- Symfony’s Maker Bundle : To help create controllers, entities, and the likes.
- Symfony’s Security system : To help with authentication and access control in the application.
- Codeception
- Codeception Asserts : This module provides helpful assertion methods to use in tests
- Codeception Doctrine2 : This module provides helpers to access the database using Doctrine. It also helps us test for the presence or absence of entities in repositories.
- Codeception PHPBrowser : This module is required by Codeception. It helps with performing web acceptance tests.
- Codeception Rest : This module simplifies the process of testing REST web services.
- Codeception Symfony : This module uses Symfony’s DomCrawler and HttpKernel Components to emulate requests and test responses. It also provides access to the dependency injection container, enabling us to grab services when necessary.
Install them using the commands below.
composer require --with-dependencies doctrine security
composer require --dev
codeception/codeception
codeception/module-asserts
codeception/module-doctrine2
codeception/module-phpbrowser
codeception/module-rest
codeception/module-symfony
fakerphp/faker
maker
When carrying out the set-up, you may view a notification that looks like the one demonstrated below.
Do you want to execute this recipe?
[y] Yes
[n] No
[a] Yes for all packages, only for the current installation session
[p] Yes permanently, never ask again for this project
(defaults to n):
When you are finished, hit the ‘y’ key and then hit the ‘Enter’ key to finish the installation process.
Update the application’s configuration
Create a duplicate of the .env file, called .env.local, which was generated by Symfony while setting up the project, by running the command indicated.
Git will not take into account this file because it corresponds to a pattern that is already listed in the .gitignore created by Symfony.
This document is in place to follow the recommended procedure of keeping your credentials confidential and secure by not storing them in the code.
Change the DATABASE_URL in .env.local so that the application uses a SQLite database instead of the default PostgreSQL. Disable the current DATABASE_URL record and make sure the SQLite selection matches the illustration under.
DATABASE_URL="sqlite:///%kernel.project_dir%/var/data.db"
A database will be designed in the var folder in the main folder of the project and it will be called data.db.
Begin the program to confirm that the alterations have been successful, utilizing the command listed here.
By default, Symfony runs on port 8000 and if you type in https://localhost:8000/ in your web browser, you will see the standard Symfony welcome page that appears similar to the displayed image.
Verify that the program is functioning properly, and then end it by pressing ctrl + c.
Generate a .env.test.local file that is duplicated from the .env.local file using the following command. Codeception will be utilizing this to create a distinct, localized testing environment configuration to deflect any unforeseen activity.
Git does not keep track of .env.test.local, like it does not keep track of .env.local.
cp .env.local .env.test.local
Change the DATABASE_URL variable in .env.test.local to be the same as the example provided. This alteration prevents the utilization of the same database in both the testing and building up surroundings.
DATABASE_URL="sqlite:///%kernel.project_dir%/var/data_test.db"
Following, go to the primary folder of the venture and open codeception.yml. Alter the params feature so that it appears as in the sample beneath.
params:
- .env.test.local
If codeception.yml is missing, type these instructions to delete the contents of the tests folder and restart Codeception.
rm -rf tests/* php vendor/bin/codecept bootstrap –namespace=App\Tests
We establish a namespace that follows the standards for excellence set by Symfony and complies with the PSR-4 namespace requirements.
Leave a Reply