yii controller action unit testing without selenium - unit-testing

Suppose I have an action:
function actionShowItem($id)
{
$item = Item::model()->findByPk($id);
$this->render("showitem",array('model' => $id));
}
What is the simple unit test for this action which will verify text in the view output. Its easy in zend framework without using selenium. We could create fake GET and POST too in zend. But I have not found same examples in Yii. Please suggest.

The Yii PHP framework is very good in many aspects but its very sad that it does not internally support any sort of simulated controller action output testing. It only has selenium based web browser methods. I came to Yii from ZendF and zend does have good testing systems including xpath based assertions. So I had to understand the code flow and code this within my components/Controller.php. It could be done without changing any core yii framework which in my opinion is the charm of Yii.
Every client code has components/Controller.php which is a common base class for all controllers in Yii. And render is a CController method which means I could override it and capture view output for use by the unit test code.
You would need a runmode param (in config/main.php) to identify if you are a testrun or a production. In production output is simply echoed while we cannot echo anything in testrun (Just spoils the unit test report). In the test code you get the output in $render_output on which you can do assert wrapper xpath or strpos checks. This hack is not the best but does the job just fine.
function render($view,$data=null,$return=false)
{
$out = parent::render($view,$data,true);
if(isset(Yii::app()->params['runmode'])
&& Yii::app()->params['runmode'] == 'test')
{
global $render_output;
return $render_output = $out;
}
if($return)
return $out;
else
echo $out;
}

Related

Zend Framework 2 unit testing using CodeCeption

We are currently using CodeCeption to do some acceptance testing, but I would like to also add unit testing to the current setup.
Has anyone done Unit testing in Zend Framework 2 project using CodeCeption? And if so, how did you set up the environment?
Found this in codeception docs: http://codeception.com/docs/modules/ZF2
There is an example in this github project: https://github.com/Danielss89/zf2-codeception
I solved this problem by using PHPUnit. I followed the Zend PHPUnit guide, http://framework.zend.com/manual/current/en/tutorials/unittesting.html, which only got me so far. But, I was able to bootstrap Zend using a phpunit.xml file and pointing that to _bootstrap.php. Codeception is also able to bootstrap my tests using the _bootstrap.php file. So, now I can run my tests using both:
phpunit --bootstrap tests/unit/_bootstrap.php
and
php vendor/bin/codecept run unit
I was surprised to see how few examples there are for this. I was writing tests for a job queue client. Here is a bit of my test:
protected function setUp()
{
$this->serviceManager = new ServiceManager(new ServiceManagerConfig());
$config = Bootstrap::getConfig();
$this->serviceManager->setService('ApplicationConfig', $config);
$this->serviceManager->get('ModuleManager')->loadModules();
$this->client = $this->serviceManager->get('jobclient');
$this->client->addServers(array(array('ip' => '127.0.0.1', 'port' => '666')));
}
public function testServiceCreateSuccess() {
$this->client->setQueue($this->testData['queue']);
$this->assertEquals($this->client->getQueue(), $this->testData['queue'], 'We get the expected queue');
}
I mocked the job client and had a local config. My _bootstrap.php file looks basically the same as the Zend example except that I am using factories defined in the service config to manage some objects. So, I performed the "setService" method in the test setup rather then in the bootsrap init.

Getting laravel & phpunit to send message when unit test fails

I am using a function that tests for existence of an array in each page. I am using laravel's function assertViewHas. This would be fine if I had a function for each page testing for this array existence, however it seems waste of time, when I could just have a foreach running the test for each page. However, if one of the page's fails the assertion, I won't know which it is, phpunit will just say the test has failed.
So, is there any way of writing the current $page in the fail error when running phpunit in the command line?
....private $guest_pages=array('/','login','signup');
....
public function testGuestViewsHaveContent ()
{
foreach ($this->guest_pages as $page) {
$response = $this->call('GET', $page);
$this->assertViewHas('content');
}
}
Have you tried echoing $_SERVER['SCRIPT_NAME'] ?
Edit:
Duh... OK this does not answer your question, I didn't understand at first.
So, assertViewHas comes from Laravel's extending PHPUnit
vendor\laravel\Illuminate\Foundation\Testing\TestCase.php and basically checks if it's a view instance, if it's an array and just $this->assertEquals for each value.
assertEquals can have a custom message as a third parameter, so what you can do is modify your test, not using assertViewhas but assertEquals in a similar way as in assertViewHas (extending PHPUnit, not Laravel's testcase) and provide your custom message.

Ordered Selenium unit tests n

I have a small problem, I have created some Selenium tests. The problem is I can't order the testcases I have created. I know unit testing should not be ordered but this is what I need in my situation. I have to follow these steps: login first, create a new customer, change some details about the customer and finally log out.
Since there is no option to order unit tests in NUnit I can't execute this.
I already tried another option, to create a unittest project in Visual Studio, because Visual Studio 2012 has the ability to create a ordered unit test. But this is not working because I can't run a unit test while I am running my ASP.NET project. Another solution file is also not a good option because I want to verify my data after it has been submitted by a Selenium test.
Does someone of you have another solution to solve my problem?
If you want to test all of those steps in a specific order (and by the sounds of it, as a single session) then really it's more like an acceptance test you are talking about; and in that case it's not a sin to write more complex test methods and Assert your conditions after each step.
If you want to test each step in true isolation (a pure unit test) then each unit test must be capable of running by itself without any reference to any other tests; but when you're testing the actual site UI itself this isn't really an option for you.
Of course if you really you want to have every single test somehow setup every single dependency without reference to any other actions (e.g in the last test you would need to fake the login token, your data layer will have to pretend that you added a new customer, etc. A lot of work for dubious benefit...)
I say this based on the assumption that you already have unit tests written for the server-side controllers, layers, models, etc, that you run without any reference to the actual site running in a browser and are therefore confident that the various back-end part of your site do what they are supposed to do
In your case I'd recommend more of a hybrid integration/acceptance test
void Login(IWebDriver driver)
{
//use driver to open browser, navigate to login page, type user/password into box and press enter
}
void CreateNewCustomer(IWebDriver driver)
{
Login(driver);
//and then use driver to click "Create Customer" link, etc, etc
}
void EditNewlyCreatedCustomer(IWebDriver driver)
{
Login(driver);
CreateNewCustomer(driver);
//do your selenium stuff..
}
and then your test methods:
[Test]
void Login_DoesWhatIExpect()
{
var driver = new InternetExplorerDriver("your Login URL here");
Login(driver);
Assert(Something);
}
[Test]
void CreateNewCustomer_WorksProperly()
{
var driver = new InternetExplorerDriver("your Login URL here");
CreateNewCustomer(driver);
Assert(Something);
}
[Test]
void EditNewlyCreatedCustomer_DoesntExplodeTheServer()
{
var driver = new InternetExplorerDriver("your Login URL here");
EditNewlyCreatedCustomer(driver);
Assert(Something);
}
In this way the order of the specific tests do not matter; certainly if the Login test fails then the CreateNewCustomer and EditNewlyCreatedCustomer tests will also fail but that's actually irrelevant in this case as you are testing an entire "thread" of operation

How do I write test(s) for this in Symfony?

I'm about to delve into test in the PHP world and I have some questions. I have a controller which handles a loan application. The bulk of the work is then delegated to a ProcessLoanApplication class.
ApplyController
class ApplyController extends Controller
{
public function indexAction(Request $request)
{
$form = $this->createForm(new LoanApplication());
if($request->getMethod() == 'POST') {
$form->bind($request);
if($form->isValid()) {
$session = $this->getRequest()->getSession();
$loan_app_processor = new Tasks\ProcessLoanApplication($form, $session);
$loan_app_processor->process();
return $this->redirect($this->generateUrl('apply_thanks'));
}
}
Tasks\ProcessLoanApplication
class ProcessLoanApplication
{
private $_QuickBaseModels;
private $_session;
private $_app; // submitted form data
private $_existingApp = false; // holds existing application in QB, if it exists
public function __construct(Form $form, Session $session)
{
$this->_app = $form->getNormData();
$this->_session = $session;
// save the form data to a session
$session->set('application', $this->_app);
// create the quickbase table models objects
$this->_QuickBaseModels['GenFnHome\Application'] = new GenFnHome\Application();
$this->_QuickBaseModels['GenFnHome\SSN'] = new GenFnHome\SSN();
}
public function process()
{
$this->_existingApp = $this->_getExistingApplication();
$app_status = $this->_existingApp[GenFnHome\SSN::LogInApplicationStatus];
if(!$this->_existingApp || ($this->_existingApp && ($app_status !== 'PENDING' && $app_status !== 'OPEN' && $app_status !== 'EXPIRED')))
return $this->_saveNewLoanApplication();
if($app_status == 'EXPIRED') $this->_reOpenApplication();
}
There's a lot going on here, so I will outline it first:
User makes a requests for the application
Application form is validated
If valid, process loan application
Check if the user already has an app, if so - do X, if not Y
The application is persisted in an 'online database' (QuickBase) that my application communicates with via XML over HTTP (in other words, there is no real db)
My questions to the community:
What should be tested here? I know it is largely up to me, but perhaps the community can recommend some baseline tests that should def be written. Should I be testing the controller, the processor class, and the QuickBase class?
Should my tests be independent of one another - meaning, I should test each component individually, rather than have one massive testApplication that does everything the indexAction does and just looks for the expected sessions vars that get set?
Finally, how does one test API calls (request / response) without actually making real request (I'm using PHPUnit).
Anything else I should know?
Thanks!
What should be tested here? I know it is largely up to me, but perhaps the community can recommend some baseline tests that should def be written. Should I be testing the controller, the processor class, and the QuickBase class?
I recommend to test every class you build. If you are using Test Driven Development, the test declares what you are building, no test no code.
Should my tests be independent of one another - meaning, I should test each component individually, rather than have one massive testApplication that does everything the indexAction does and just looks for the expected sessions vars that get set?
Every Unit test should be isolated and should only test the Class that you are testing. You should use Mock object (use the PHPunit mock library or other 3th party libraries as Mockery) if one object dependences on another object.
Finally, how does one test API calls (request / response) without actually making real request (I'm using PHPUnit).
You can use the Symfony's WebTestCase that provides easy methods to imitate a browser request, learn more about that in the documentation. We call that Functional Testing.
This is usually the stage after Unit Testing. In Unit Testing you will test each individual class (it's a good practice to unit test your controller) and after that you write your Functional Tests which combines everything and tests if it works like expected.
for Controllers you should use functional tests (http://symfony.com/doc/2.0/book/testing.html#functional-tests). With them you can emulate browser and user's actions like submiting form and checking validation, database changes, http status codes and so on.
You should not forget to unit test ProcessLoanApplication.
I dont realy know why you pass form object to ProcessLoanApplication anyway. You should pass entity - it has normdata already.

How do I Unit Test the Output of a View in MonoRail?

I've been trying to write some initial NUnit unit tests for MonoRail, having got some basics working already. However, while I've managed to check whether a Flash["message"] value has been set by a controller action, the BaseControllerTest class doesn't seem to store the output for a view at all, so whether I call RenderView or the action itself, nothing gets added to the Response.OutputContent data.
I've also tried calling InPlaceRenderView to try to get it to write to a StringWriter, and the StringWriter also seems to get nothing back - the StringBuilder that returns is also empty.
I'm creating a new controller instance, then calling
PrepareController(controller,"","home","index");
So far it just seems like the BaseControllerTest is causing any output to get abandoned. Am I missing something? Should this work? I'm not 100% sure, because while I'm also running these unit tests in MonoDevelop on Linux, although MonoRails is working OK there.
While I haven't got an ideal method for testing Views, this is possibly less important when ViewComponents can be tested adequately. To test views within the site itself, I can use Selenium. While in theory that can be made part of an NUnit test suite, that didn't run successfully under MonoDevelop in my tests (failing to start the connection to Selenium RC consistently, despite the RC interactive session working fine). However, the Selenium tests can be run as a set from Firefox, which is not too bad - unit testing with NUnit, then Integration/System testing scripting using a Selenium suite, and that setup will work in a Linux/MonoDevelop setup.
As for testing the underlying elements, you can check for redirections and check the flash value set or the like, so that's all fine, and for testing ViewComponents the part-mocked rendering does return the rendered output in an accessible form, so they've proved much easier to test in NUnit (with a base test class of BaseViewComponentTest) as follows:
[Test]
public void TestMenuComponentRendersOK()
{
var mc = new MenuComponent();
PrepareViewComponent(mc);
var dict = new System.Collections.Specialized.ListDictionary();
dict.Add("data",getSampleMenuData());
dict.Add("Name","testmenu");
// other additional parameters
mc.RenderComponent(mc,dict);
Assert.IsTrue(this.Output.Contains(""),"List items should have been added");
}