<commit_before>####################
HTTP Feature Testing
####################

Feature testing allows you to view the results of a single call to your application. This might be returning the
results of a single web form, hitting an API endpoint, and more. This is handy because it allows you to test the entire
life-cycle of a single request, ensuring that the routing works, the response is the correct format, analyze the results,
and more.

.. contents::
    :local:
    :depth: 2

The Test Class
==============

Feature testing requires that all of your test classes extend the ``CodeIgniter\Test\FeatureTestCase``
class or use the ``CodeIgniter\Test\FeatureTestTrait``. Since these testing tools extend
`CIDatabaseTestCase <database.html>`_ you must always ensure that ``parent::setUp()`` and ``parent::tearDown()``
are called before you take your actions.
::

    <?php namespace App;

    use CodeIgniter\Test\FeatureTestCase;

    class TestFoo extends FeatureTestCase
    {
        public function setUp(): void
        {
            parent::setUp();
        }

        public function tearDown(): void
        {
            parent::tearDown();
        }
    }

Requesting A Page
=================

Essentially, the FeatureTestCase simply allows you to call an endpoint on your application and get the results back.
to do this, you use the ``call()`` method. The first parameter is the HTTP method to use (most frequently either GET or POST).
The second parameter is the path on your site to test. The third parameter accepts an array that is used to populate the
superglobal variables for the HTTP verb you are using. So, a method of **GET** would have the **$_GET** variable
populated, while a **post** request would have the **$_POST** array populated.
::

    // Get a simple page
    $result = $this->call('get', site_url());

    // Submit a form
    $result = $this->call('post', site_url('contact'), [
        'name' => '<NAME>',
        'email' => '<EMAIL>'
    ]);

Shorthand methods for each of the HTTP verbs exist to ease typing and make things clearer::

    $this->get($path, $params);
    $this->post($path, $params);
    $this->put($path, $params);
    $this->patch($path, $params);
    $this->delete($path, $params);
    $this->options($path, $params);

.. note:: The $params array does not make sense for every HTTP verb, but is included for consistency.

Setting Different Routes
------------------------

You can use a custom collection of routes by passing an array of "routes" into the ``withRoutes()`` method. This will
override any existing routes in the system::

    $routes = [
       [ 'get', 'users', 'UserController::list' ]
     ];

    $result = $this->withRoutes($routes)
        ->get('users');

Each of the "routes" is a 3 element array containing the HTTP verb (or "add" for all),
the URI to match, and the routing destination.


Setting Session Values
----------------------

You can set custom session values to use during a single test with the ``withSession()`` method. This takes an array
of key/value pairs that should exist within the $_SESSION variable when this request is made, or ``null` to indicate
that the current values of ``$_SESSION`` should be used. This is handy for testing authentication and more.
::

    $values = [
        'logged_in' => 123
    ];

    $result = $this->withSession($values)
        ->get('admin');
    
    // Or...
    
    $_SESSION['logged_in'] = 123;
    
    $result = $this->withSession()->get('admin');

Bypassing Events
----------------

Events are handy to use in your application, but can be problematic during testing. Especially events that are used
to send out emails. You can tell the system to skip any event handling with the ``skipEvents()`` method::

    $result = $this->skipEvents()
        ->post('users', $userInfo);


Testing the Response
====================

Once you've performed a ``call()`` and have results, there are a number of new assertions that you can use in your
tests.

.. note:: The Response object is publicly available at ``$result->response``. You can use that instance to perform
    other assertions against, if needed.

Checking Response Status
------------------------

**isOK()**

Returns a boolean true/false based on whether the response is perceived to be "ok". This is primarily determined by
a response status code in the 200 or 300's.
::

    if ($result->isOK())
    {
        ...
    }

**assertOK()**<commit_msg>Added documentation of the withBodyFormat method in feature tests.<commit_after>####################
HTTP Feature Testing
####################

Feature testing allows you to view the results of a single call to your application. This might be returning the
results of a single web form, hitting an API endpoint, and more. This is handy because it allows you to test the entire
life-cycle of a single request, ensuring that the routing works, the response is the correct format, analyze the results,
and more.

.. contents::
    :local:
    :depth: 2

The Test Class
==============

Feature testing requires that all of your test classes extend the ``CodeIgniter\Test\FeatureTestCase``
class or use the ``CodeIgniter\Test\FeatureTestTrait``. Since these testing tools extend
`CIDatabaseTestCase <database.html>`_ you must always ensure that ``parent::setUp()`` and ``parent::tearDown()``
are called before you take your actions.
::

    <?php namespace App;

    use CodeIgniter\Test\FeatureTestCase;

    class TestFoo extends FeatureTestCase
    {
        public function setUp(): void
        {
            parent::setUp();
        }

        public function tearDown(): void
        {
            parent::tearDown();
        }
    }

Requesting A Page
=================

Essentially, the FeatureTestCase simply allows you to call an endpoint on your application and get the results back.
to do this, you use the ``call()`` method. The first parameter is the HTTP method to use (most frequently either GET or POST).
The second parameter is the path on your site to test. The third parameter accepts an array that is used to populate the
superglobal variables for the HTTP verb you are using. So, a method of **GET** would have the **$_GET** variable
populated, while a **post** request would have the **$_POST** array populated.
::

    // Get a simple page
    $result = $this->call('get', site_url());

    // Submit a form
    $result = $this->call('post', site_url('contact'), [
        'name' => 'Fred Flintstone',
        'email' => '<EMAIL>'
    ]);

Shorthand methods for each of the HTTP verbs exist to ease typing and make things clearer::

    $this->get($path, $params);
    $this->post($path, $params);
    $this->put($path, $params);
    $this->patch($path, $params);
    $this->delete($path, $params);
    $this->options($path, $params);

.. note:: The $params array does not make sense for every HTTP verb, but is included for consistency.

Setting Different Routes
------------------------

You can use a custom collection of routes by passing an array of "routes" into the ``withRoutes()`` method. This will
override any existing routes in the system::

    $routes = [
       [ 'get', 'users', 'UserController::list' ]
     ];

    $result = $this->withRoutes($routes)
        ->get('users');

Each of the "routes" is a 3 element array containing the HTTP verb (or "add" for all),
the URI to match, and the routing destination.


Setting Session Values
----------------------

You can set custom session values to use during a single test with the ``withSession()`` method. This takes an array
of key/value pairs that should exist within the $_SESSION variable when this request is made, or ``null` to indicate
that the current values of ``$_SESSION`` should be used. This is handy for testing authentication and more.
::

    $values = [
        'logged_in' => 123
    ];

    $result = $this->withSession($values)
        ->get('admin');
    
    // Or...
    
    $_SESSION['logged_in'] = 123;
    
    $result = $this->withSession()->get('admin');

Bypassing Events
----------------

Events are handy to use in your application, but can be problematic during testing. Especially events that are used
to send out emails. You can tell the system to skip any event handling with the ``skipEvents()`` method::

    $result = $this->skipEvents()
        ->post('users', $userInfo);

Formatting The Request
-----------------------

You can set the format of your request's body using the ``withBodyFormat()`` method. Currently this supports either
`json` or `xml`. This will take the parameters passed into ``call(), post(), get()...`` and assign them to the
body of the request in the given format. This is useful when testing JSON or XML API's so that you can set the request
in the form that the controller will expect.
::

    //If your feature test contains this:
    $result = $this->withBodyFormat('json')
        ->post('users', $userInfo);

    //Your controller can then get the parameters passed in with:
    $userInfo = $this->request->getJson();

Testing the Response
====================

Once you've performed a ``call()`` and have results, there are a number of new assertions that you can use in your
tests.

.. note:: The Response object is publicly available at ``$result->response``. You can use that instance to perform
    other assertions against, if needed.

Checking Response Status
------------------------

**isOK()**

Returns a boolean true/false based on whether the response is perceived to be "ok". This is primarily determined by
a response status code in the 200 or 300's.
::

    if ($result->isOK())
    {
        ...
    }

**assertOK()**
{'fraction_non_alphanumeric': 0.13814903098766712, 'fraction_numerical': 0.0029018551145196395, 'mean_word_length': 3.1883680555555554, 'pattern_counts': {'":': 0, '<': 10, '<?xml version=': 0, '>': 54, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 1}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3005250', 'n_tokens_mistral': 2555, 'n_tokens_neox': 2458, 'n_words': 1196}