2
\$\begingroup\$

I want to unit test my PHP website, which uses the outdated framework CodeIgniter 3. CodeIgniter 3 uses the MVC pattern, and each page on the website corresponds to a public method in a controller.

I wrote a simple unit test (to check and see if the page's title is set to "About Us") for one of the pages/methods as a proof of concept. It tests this line:

$this->data = set_page_title('About Us', $this->data);

... by making this assertion...

$this->assertEquals( $titleGlobal, 'About Us' );

But boy does it take a lot of setup code to test this simple assertion.

I got the test for this working, but it's ugly. I suspect code review is going to reveal...

  1. instead of creating stubs at the top of the file, I should do it with native PHPUnit methods such as $this->createMock(), and

  2. I need to replace all my functions with dependency injected classes and methods, for easier stubbing/mocking

Would like someone good at PHPUnit to confirm this and add some details. Thanks a lot :)

The name of the controller is General, and the name of the page/method is about_us.

Just looking for a code review of tests/controllers/GeneralTest.php. Some other code is included for background.

tests/controllers/GeneralTest.php

<?php use PHPUnit\Framework\TestCase; if ( ! defined('BASEPATH') ) { define('BASEPATH', true); } class CI_Controller { function __construct() { $this->output = new Output(); } } class Output { function enable_profiler() { // intentionally blank since this is a test double } } function set_auth_variables( $data ) { // intentionally blank since this is a test double } function load_page_with_great_races_sidebar( $uri, $data ) { // intentionally blank since this is a test double } $titleGlobal = ''; function set_page_title( $title, $data ) { global $titleGlobal; $titleGlobal = $title; } final class GeneralTest extends TestCase { protected function setUp(): void { define( 'APPPATH', 'application/' ); define( 'ENVIRONMENT', 'development' ); require_once( 'application/config/constants.php' ); require_once( 'application/controllers/General.php' ); } public function test__about_us__has_correct_title() { global $titleGlobal; $controller = new General(); $controller->about_us(); $this->assertEquals( $titleGlobal, 'About Us' ); } } 

application/controllers/General.php

<?php require_once APPPATH . 'core/MV_Base_Class.php'; class General extends MV_Base_Class { public function __construct() { parent::__construct(); } public function about_us() { $this->data = set_page_title('About Us', $this->data); load_page_with_great_races_sidebar('general/about_us', $this->data); } // ... } 

application/core/MV_Base_Class.php

<?php class MV_Base_Class extends CI_Controller { protected $data = null; public function __construct() { // Load system/core/Controller, which loads system/core/Loader. This creates the database connection and loads helper files, among other things. // If there is an SQL connection problem, the line below is where the code will die. parent::__construct(); $this->data = set_auth_variables($this->data); // If ?r= is used in the link, record this in a session variable. ("r" stands for referrer and is a marketing tracking variable.) Later, if the person creates a company or signs up to volunteer, the "r" variable will be associated with their signup, which helps populate /managers/signup_sources and similar reports. if ( isset($_GET['r']) ) { $_SESSION['http_referrer'] = $_GET['r']; } if ( ENVIRONMENT == 'development' ) { $this->output->enable_profiler(true); } // Adds 0.04s to every page load if ( ENVIRONMENT == 'development' ) { require_once('../ci_dev/vendor/autoload.php'); } else { require_once('../ci_main/vendor/autoload.php'); } } } 

application/helpers/mv_login_helper.php

<?php if ( ! defined('BASEPATH')) { exit('No direct script access allowed'); } function set_auth_variables($data) { $obj = &get_instance(); $manager_id = $obj->session->userdata('manager_id'); // Set auth variables even if user isn't logged in. That way you don't get "variable not set" errors, and we save ourselves from having to check for that. $data['auth'] = null; if ( $manager_id ) { $data['auth']['manager'] = $obj->manager_model->get_manager_by_id($manager_id); if ( $data['auth']['manager'] ) { $company_id = $data['auth']['manager']['manager_company_id']; $data['auth']['company'] = $obj->company_model->get_company_by_id($company_id); } else { $data['auth'] = null; } } if ( isset($_SESSION['admin_sticky_company_id']) ) { if ( is_admin($data['auth']) && $_SESSION['admin_sticky_company_id'] != $data['auth']['company']['company_id'] ) { $data['auth']['company'] = $obj->company_model->get_company_by_id($_SESSION['admin_sticky_company_id']); } } // If company has been deleted, unset admin_sticky_company_id to avoid Control Panel redirect loop. if ( ! isset($data['auth']['company']) && isset($_SESSION['admin_sticky_company_id']) ) { unset($_SESSION['admin_sticky_company_id']); } return $data; } // ... 

application/helpers/mv_view_helper.php

<?php if ( ! defined('BASEPATH')) { exit('No direct script access allowed'); } function load_page_with_great_races_sidebar($page_name, $data) { // $data is required. If the user is logged in, we need the auth variables. $data = html_escape($data); $obj = &get_instance(); $data['great_races'] = $obj->race_model->get_upcoming_sidebar_races(); $obj->load->view('templates/header', $data); $obj->load->view('templates/great_events_header', $data); $obj->load->view($page_name, $data); $obj->load->view('templates/great_events_footer', $data); $obj->load->view('templates/footer', $data); } // ... 

application/helpers/mv_misc_helper.php

<?php if ( ! defined('BASEPATH')) { exit('No direct script access allowed'); } function set_page_title($page_title, $data) { $data['page_title'] = $page_title . ' - ' . MV_BROWSER_TITLE; return $data; } // ... 

PHPUnit output

enter image description here

\$\endgroup\$
0

    0

    Start asking to get answers

    Find the answer to your question by asking.

    Ask question

    Explore related questions

    See similar questions with these tags.