I am having problems with testing sometimes. The test is executed, i can even get the results of the action but the test doesn't show its green or red message telling me if the test run successfully or not. (28/28 test methods complete: 28 passes, 0 fails, 95 assertions and 0 exceptions...), the time, peak memory... etc.
For example. Only if i try to execute this test it crashes:
public function testGetHotVideo() {
$result = $this->testAction("/posts/getHotVideo/");
$this->assertEquals($result, 'GoG_Tv5G17M');
}
It calls this method:
public function getHotVideo(){
$video = $this->Post->getHotVideo();
return $video[0][0]['video'];
}
And it returns the video string correctly. So it can even be printed on the test variable $result.
What's going on here?
UPDATE
I have also notice it occurs the same when i call a method which redirects to some view.
In this case, with the default delete created with Cake Bake:
public function delete($id = null){
$this->Comment->id = $id;
if (!$this->Comment->exists()) {
throw new NotFoundException(__('Invalid comment'));
}
if ($this->Comment->delete()) {
$this->Session->setFlash(__('Comment deleted'));
return $this->redirect(array('controller' => 'posts', 'action' => 'index'));
}
$this->Session->setFlash(__('Comment was not deleted'));
return $this->redirect(array('controller' => 'posts', 'action' => 'index'));
}
Is this SimpleTest (cakephp <= 1.3) or PHPUnit (>1.3)?
In case its SimpleTest you have to handle the redirects in your controller tests (read Mark Storys article on controller testing). Basically you build a test class which inherits from the controller class you are testing and than you overwrite the redirect and some other methods (here some from the article):
class TestPostsController extends PostsController {
...
function redirect($url, $status = null, $exit = true) {
$this->redirectUrl = $url;
}
...
}
than you use this modified controller in your test case.
With CakePHP 2 and PHPUnit I even had some issues with calling the header() function in some of the layouts, which broke the html structure and the css of the test-sides.
Related
We have some TypeScript code using the Aurelia framework and Dialog plugin that we are trying to test with Jasmine, but can't work out how to do properly.
This is the source function:
openDialog(action: string) {
this._dialogService.open({ viewModel: AddAccountWizard })
.whenClosed(result => {
if (!result.wasCancelled && result.output) {
const step = this.steps.find((i) => i.action === action);
if (step) {
step.isCompleted = true;
}
}
});
}
We can create a DialogService spy, and verify the open method easily - but we can't work out how to make the spy invoke the whenClosed method with a mocked result parameter so that we can then assert that the step is completed.
This is the current Jasmine code:
it("opens a dialog when clicking on incomplete bank account", async done => {
// arrange
arrangeMemberVerificationStatus();
await component.create(bootstrap);
const vm = component.viewModel as GettingStartedCustomElement;
dialogService.open.and.callFake(() => {
return { whenClosed: () => Promise.resolve({})};
});
// act
$(".link, .-arrow")[0].click();
// assert
expect(dialogService.open).toHaveBeenCalledWith({ viewModel: AddAccountWizard });
expect(vm.steps[2].isCompleted).toBeTruthy(); // FAILS
done();
});
We've just recently updated our DialogService and ran into the same issue, so we've made this primitive mock that suited our purposes so far. It's fairly limited and doesn't do well for mocking multiple calls with different results, but should work for your above case:
export class DialogServiceMock {
shouldCancelDialog = false;
leaveDialogOpen = false;
desiredOutput = {};
open = () => {
let result = { wasCancelled: this.shouldCancelDialog, output: this.desiredOutput };
let closedPromise = this.leaveDialogOpen ? new Promise((r) => { }) : Promise.resolve(result);
let resultPromise = Promise.resolve({ closeResult: closedPromise });
resultPromise.whenClosed = (callback) => {
return this.leaveDialogOpen ? new Promise((r) => { }) : Promise.resolve(typeof callback == "function" ? callback(result) : null);
};
return resultPromise;
};
}
This mock can be configured to test various responses, when a user cancels the dialog, and scenarios where the dialog is still open.
We haven't done e2e testing yet, so I don't know of a good way to make sure you wait until the .click() call finishes so you don't have a race condition between your expect()s and the whenClosed() logic, but I think you should be able to use the mock in the test like so:
it("opens a dialog when clicking on incomplete bank account", async done => {
// arrange
arrangeMemberVerificationStatus();
await component.create(bootstrap);
const vm = component.viewModel as GettingStartedCustomElement;
let mockDialogService = new MockDialogService();
vm.dialogService = mockDialogService; //Or however you're injecting the mock into the constructor; I don't see the code where you're doing that right now.
spyOn(mockDialogService, 'open').and.callThrough();
// act
$(".link, .-arrow")[0].click();
browser.sleep(100)//I'm guessing there's a better way to verify that it's finished with e2e testing, but the point is to make sure it finishes before we assert.
// assert
expect(mockDialogService.open).toHaveBeenCalledWith({ viewModel: AddAccountWizard });
expect(vm.steps[2].isCompleted).toBeTruthy(); // FAILS
done();
});
I am a beginner in unit testing in CakePHP. My version of CakePHP is 2.5.2 and I am using cake test suite 2.5.2. I want to do controller testing. I have tried all methods given in cookbook. I think this is too much complex code for given example in cookbook. I have customised routes for my application. I can invoke method showed below by calling : http://localhost/api/v1/networks/
This is the simplest method in my controller. How can i start testing on this method and what mocks should i need?
public function index() {
if (!$this->request->is('Get')) {
throw new MethodNotAllowedException(__('HTTP request Method Not allowed..'));
}
$networks = $this->Network->UserNetwork->getNetworks($this->current_user);
$returnObject = array('message' => 'Networks found successfully',
'data' => $networks
);
return $this->_sendResponse($returnObject, 200);
}
what i have tried so far is:
<?php
App::uses('NetworksController', 'Controller');
class NetworksControllerTestCase extends ControllerTestCase {
public $fixtures = array(
'app.network',
);
public function setUp() {
parent::setUp(); // TODO: Change the autogenerated stub
$this->Network = $this->generate('Networks',
array('models' => array('network','usernetwork' => array('getNetworks'))));
}
public function testIndex() {
$result = $this->testAction('/api/v1/networks/index', array('return' => 'vars'));
debug($result);
}}
I start to write unittests for controllers in cakephp 2.5 and struggle with a testmethod for the the delete method.
Controller:
public function delete($id = null)
{
$this->Content->id = $id;
if (!$this->Content->exists()) {
throw new NotFoundException(__('Invalid content'));
}
$this->Content->recursive = -1;
$content = $this->Content->findById($id);
debug($content);
if ($this->Content->delete($id)) {
$this->Session->setFlash(__('Content deleted'), 'flash_success');
return $this->redirect(array('action' => 'search'));
} else {
$this->Session->setFlash(__('Content was not deleted'), 'flash_error');
return $this->redirect(array('action' => 'view', $id));
}
}
Test:
$this->testAction(
'/contents/delete/11');
$this->assertContains('/contents/search', $this->headers['Location']);
However the $this->headers['Location'] is just myDomain.com instead of myDomain.com/contents/search although the Content with id 11 is in my testDB. So of course my test fails. By the way deleting in my real application works, so I guess the problem is the assertion and not the code of my app.
What would be the correct approach for a test of the delete method?
Do you send back proper HTTP status code headers?
Typically I test that the delete returned a HTTP 204: No Content header
I'm just starting out with TDD and I'm trying to test validation in a model. Currently, all my tests pass, but I believe that they shouldn't. The testBodyIsRequiredToValidate() should fail since I have not added it to the rules array. When I remove the title required from the rules array, all my tests fail as expected. However, adding title required causes them all to pass.
My Tests:
class PostTest extends TestCase {
public function testTitleIsRequiredToValidate()
{
$post = new Post;
$post->body = 'foobar';
$this->assertFalse($post->validate(compact('post')));
}
public function testBodyIsRequiredToValidate()
{
$post = new Post;
$post->title = 'foobar';
$this->assertFalse($post->validate(compact('post')));
}
}
My Model:
class Post extends Eloquent {
public function validate($input)
{
$rules = array(
'title' => 'required'
);
$v = Validator::make($input, $rules);
if ($v->passes()) {
return true;
} else {
return false;
}
}
}
This happens because your validation is wrong. You pass the model itself to the validator but you should pass an associative array.
When you delete title from the rules you pass an empty rule, so the validation will be always true, thats why all your test fail.
When you add title to your rules, the validations will be always false hence all your tests pass, because the validator's first parameter doesn't have an array element called title.
One way to correct this is to change your validation line to this:
$v = Validator::make(array('title' => $input['post']->title), $rules);
Here you get the $post from your $input array, because compact() will put it to an array with the key of the variable name. Then you access the title property of the object and make this to the value of the title key so the validator will be able to find and validate it.
I have to mention that your validation technique is kinda bad for me. You pass the object in an array to a function of the same object. Why don't you get the object in the function?
I would write the validator function like this:
public function validate() {
$rules = array(
'title' => 'required'
);
$v = Validator::make($this->toArray(), $rules);
return $v->passes();
}
So I could call the function like this in the tests:
$this->assertFalse($post->validate());
in the controller there is an action:
def delete = {
withDomain {
it.delete()
flash.message = "${message(code: 'default.deleted.message', args: [message(code: 'chocolateBar.label', default: 'ChocolateBar'), it.name])}"
redirect action: 'list'
}
}
which can be tested in development. while in unit test, the message(..) method throws exception ( groovy.lang.MissingMethodException: No signature of method: longtest.ChocolateBarController.message() is applicable for argument types: (java.util.LinkedHashMap) values: [[code:chocolateBar.label, default:ChocolateBar]]):
public void testDelete() {
controller.params.id = '3'
controller.delete()
assert 'list'==controller.redirectArgs.action
}
After study, a mockTagLib method should be called during setup. But found no correct class name for built-in message(..). Please help.
I've solved the problem in unit controller test. like this:
//This is inside Spock test
#Shared
ResourceBundleMessageSource messageSource = null
#Shared
Closure mockMessage = {Map map ->
return messageSource.getMessage((String)map.code, (Object[])map.args, Locale.default)
}
def setupSpec(){
URL url = new File('grails-app/i18n').toURI().toURL()
messageSource = new ResourceBundleMessageSource()
messageSource.bundleClassLoader = new URLClassLoader(url)
messageSource.basename = 'messages'
messageSource.setDefaultEncoding("utf-8")
}
def setup(){
controller.metaClass.message = mockMessage
}
This code is for spock test, but main idea is also available for normal grails test.
In running phase(not test),
calling "message" in controller class results in calling "message" of ValidationTagLib class,
but they are not bind in unit test phase.
So I made almost same logic of "message" of ValidationTagLib,
and bind it(named "mockMessage") to controller.message.
With this code, you can execute "message" correctly in controller class in test.