# Maintainable Tests With Data Providers & Callable in Laravel

Testing is an essential component of the software development process that helps to maintain product quality and reliability. It involves the **verification** and **validation of software products** to ensure they meet the intended requirements and function as expected.

The key features of testing include **early detection of bugs and errors**, which helps to save time and resources by identifying and resolving issues early on in the development cycle. Testing also helps to ensure that **end-user expectations** are met, which is critical for achieving customer satisfaction. In addition, testing can lead to the **improvement of code quality and maintain code integrity** without system failure. Finally, testing enables system delivery with confidence, as it ensures that the software is thoroughly tested and ready for deployment.

### Testing in Laravel

Laravel offers a built-in framework for testing that simplifies the process of writing and executing tests for an application. Among its features is **data-driven testing**, which involves passing a set of test data as parameters to the test method using a **data provider method**.

---

### Testing with Data providers

In Laravel, we can pass sets of test data with data providers, minimizing the need for tests on different cases. Generally, we use data providers to pass the test cases and the expected value to be asserted.

```php
<?php
namespace Tests\Unit;
use PhpUnit\Framework\TestsCase;
class LarvelTest extends TestCase
{    
    public function oddEvenTestDataProvider(){
        return [
            [1,'odd'],
            [2,'even'],
            [-11,'odd'],
            [-10,'even'],
        ];
    }

    /**
    * @param int $value
    * @param string $expected
    * 
    * @dataprovider oddEvenTestDataProvider
    */
    public function testOddEven($value,$expected){
        $integerType= $value % 2 == 0?'even':'odd';
        $this->assertEquals($expected,$integerType);
    }
}
```

In this instance, we provided test cases to verify both even and odd integers, as well as a few positive and negative integers, along with their expected outcomes. The `oddEvenTestDataProvider` test then checks whether the actual results match the expected values.

The main test function may become cluttered with a large number of manipulations needed to perform assertions based on test data. However, this issue can be resolved by using data providers to define assertions within callables, which ultimately results in cleaner and more organized testing code.

---

### Data providers with callables

With the regular test, we can set test data and expectations to assert. But sometimes, we might need to conduct assertions dynamically based on a condition. There might come situations, where code with multiple conditions just to decide what to be asserted for data. This will make test complex to keep track of what is being tested. To manage this possible complexity, callables can be used with data providers. With **callable** we can establish an intended assertion per test data. This process will help the test focus on assertion, meanwhile, data providers decide what assertion to carry out.

Data providers are executed before the Laravel service container is fully operational, which means that certain framework-related features may not be available when defining test data. Nevertheless, by using **callables**, it is possible to ensure that the required framework features are made available for execution during testing.

Here is an example of a test using data providers with callables.

```php
<?php

namespace Tests\Unit\Http\Services\Common;

use Generator;
use Tests\TestCase;

class OddEvenTest extends TestCase
{

    public function integerProvider(): Generator
    {        
            #test-data-1
            yield [
                1,
                function ($value) {
                    $this->assertEquals('odd', 
                                $this->getIntegerType($value));
                    $this->assertEquals('positive', 
                                $this->getIntegerIndex($value));
                }
            ];

            #test-data-2
            yield [
                0,
                function ($value) {
                    $this->assertEquals('even', 
                                $this->getIntegerType($value));
                }
            ];

            #test-data-3
            yield [
                -2,
                function ($value) {
                    $this->assertEquals('even', 
                            $this->getIntegerType($value));
                    $this->assertEquals('negative', 
                    $this->getIntegerIndex($value));
                }
            ];

    }

    /**
     * @dataProvider integerProvider
     */
    public function testIntegerIndexAndType(
                            int $value, 
                            callable $assert)    
    {
        $assert($value);
    }

    private function getIntegerIndex($value): string
    {
        if($value > 0)return 'positive';
        if($value < 0)return 'negative';
        return '';
    }

    private function getIntegerType($value): string
    {
        return $value % 2 == 0 ? 'even' : 'odd';
    }
}
```

As demonstrated in the example above, the test verifies both the type and index of the integer test data. Each set of data supplied via the `integerProvider` includes an integer value and a callable that contains a unique assertion that distinguishes it from other test cases.

Here,

**#test-data-1**: The data provide **1** as a value along with the callable function that asserts it is a **positive** and odd **number**.

**#test-data-2**: The data provide **0** as a value along with the callable function that asserts it is an **even** number. Here assertion for integer index is not done as the `getIntegerIndex` function returns an empty string for value **0** which could be ignored, thus showcasing the benefit of callable usage with the test.

**#test-data-3**: The data provide **2** as a value along with the callable that asserts it is a `negative` and `even` number. Here also the assertions defined completely differ from other test data.

---

### Conclusion

To conclude, there are ways to write maintainable tests 👨‍💻, and one effective method is by using data providers with callables. As demonstrated above, this practice isolates the main test function and delegates the responsibility of determining assertions to the data providers.

Although there may be tradeoffs to using this approach, they can be mitigated by careful implementation and can ultimately add value to the test suite. While the example may not perfectly reflect a real-world scenario, it provides a good starting point to develop a better test suite. 🚀  
  
If you have any additional ideas, suggestions, or feedback regarding the topic discussed or any other related matter, please do not hesitate to share them with us. So, feel free to share your thoughts!

Thank you and Keep on learning.🚀🚀
