CakePHP Unit Testing Mock Controller - unit-testing

I am trying to test that a value is entered into the session component with CakePHP Mock objects, but my test keeps failing stating that the write method has not been called. I have debugged this and know for a fact the method has been called, so not sure what I am doing wrong.
Here is the code in the unit test:
$this->controller = $this->generate('Posts', array(
'components' => array(
'Session' => array('write')
)));
$this->testAction(...);
$this->controller->Session
->expects($this->any())
->method('write')
->with('my value here');
I get the following error:
Expectation failed for method name is equal to when invoked zero or more times.
Mocked method does not exist.
If I change the call to expects to be ->expects($this->once()) I then get this error:
Expectation failed for method name is equal to when invoked 1 time(s).
Method was expected to be called 1 times, actually called 0 times.
I did a var_dump on $this->Controller, and there is definitely a mocked session object, and it does appear to notice the call to the 'write' method, so I'm really not sure why I'm getting the error message.
Any advice would be appreciated!

The mock set-up code should be before the call to $this->testAction(), as in:
$this->controller = $this->generate('Posts', array(
'components' => array(
'Session' => array('write')
)));
//Add the method mock details here
$this->controller->Session
->expects($this->any())
->method('write')
->with('my value here');
//Then call testAction
$this->testAction(...);
That should solve your problem.

Related

How to spyOn window.location.assign on Vuejs Application with Jest?

I am needing to spyOn window.location.assign for my unit test. But when I run the test I get this error.
Cannot spy the assign property because it is not a function; undefined given instead
Here is my code:
jest.spyOn(window.location, "assign");
Could anyone give me some hints or solutions on this case?
Since Jest v25 (Which uses a newer version of JSDOM) you will get the following error:
TypeError: Cannot assign to read only property 'assign' of object '[object Location]'
This is not a Jest/JSDOM bug by the way. This is normal browser behaviour and JSDOM tries to act like a real browser.
A workaround is to remove the location object, create your own one and after running your tests you should reset it to the original location object:
describe('My awesome unit test', () => {
// we need to save the original object for later to not affect tests from other files
const realLocation = global.location
beforeAll(() => {
delete global.location
global.location = { assign: jest.fn() }
// or even like this if you are also using other location properties (or if TypeScript complains):
// global.location = { ...realLocation, assign: jest.fn() }
})
afterAll(() => {
global.location = realLocation
})
it('should call location.assign', () => {
// ...your test code
expect(global.location.assign).toHaveBeenCalled()
// or even better:
// expect(global.location.assign).toHaveBeenCalledWith('/my_link')
})
})
As window can only be accessed through the global keyword in jest tests and window.location.assign is not implemented in jsdom, you can try
jest
.spyOn(global.location, "assign")
.mockImplementation(url => console.log(url))

Why JEST mock module's async function does not need async or assertion or resolves

All,
I am new to Jest, the Testing Async Code section has made enough confuses to me, it provides so many ways to handle same case:
[1] done()
[2] expect.assertions(1) + return Promise
[3] expect.assertions(1) + return expect.resolves
[4] async callback + await + NO return expect
And the thing even more confuse is when I get to Mock Functions section:
The mock module section:
//users.test.js
import axios from 'axios';
import Users from './users';
jest.mock('axios');
test('should fetch users', () => {
const resp = {data: [{name: 'Bob'}]};
axios.get.mockResolvedValue(resp);
// or you could use the following depending on your use case:
// axios.get.mockImplementation(() => Promise.resolve(resp))
return Users.all().then(users => expect(users).toEqual(resp.data));
});
I wonder which way of those 4 ways is used here? Any detail?
The test is returning a Promise.
That corresponds to [2] expect.assertions(1) + return Promise.
Just note that you only need to use expect.assertions if you expect the Promise to reject and are using a catch:
If you expect a promise to be rejected use the .catch method. Make sure to add expect.assertions to verify that a certain number of assertions are called. Otherwise a fulfilled promise would not fail the test.
If the Promise is expected to resolve then you can do your assertions in a then like in the code sample and simply return the resulting Promise:
Just return a promise from your test, and Jest will wait for that promise to resolve. If the promise is rejected, the test will automatically fail.

Angular 2 RC5 Unit Testing - ComponentFixture detectChanges() not working

I'm running into a unit testing issue with Angular 2 RC5 where it doesn't appear that the ComponentFixture detectChanges method is working the way I expect it to. Here is a snippet of my code:
describe('Component Test 1', () => {
beforeEach(async(() => {
TestBed.compileComponents();
}));
it('should calculate base price', async(() => {
var fixture = TestBed.createComponent(CalcComponent);
console.log('Got here 1.');
fixture.detectChanges();
console.log('Got here 2.');
expect(fixture.componentInstance.calculateBasePrice()).toBeGreaterThan(50);
}));
});
Essentially, when this unit test is run, 'Got here 1.' is printed but the test never goes on to print the second 'Got here 2.' line, and I don't get any sort of useful error message regarding what the error might be. All I get is a very generic message telling me the test failed, even though it clearly never got to the expectation line seeing as how the second console.log line is never printed:
SUMMARY:
√ 9 tests completed
× 1 test failed
FAILED TESTS:
CalcComponent Tests
Component Test 1
× should calculate base price
PhantomJS 2.1.1 (Windows 7 0.0.0)
detectChanges
detectViewChildrenChanges
detectChangesInternal
detectChanges
detectChanges
detectViewChildrenChanges
detectChangesInternal
detectChanges
detectChanges
detectChanges
...
I know the problem is around the fixture.detectChanges() line because if I do comment it out, the second console.log prints, and I get an error regarding the expectation failing because of an undefined object (probably since fixture.detectChanges() was never called). Does anyone have an idea of what might be going on here?

CakePHP: calling testAction to a json-returning method causes missing view exception

What am I missing here? Here is my controller code:
public function calculate() {
$this->set(array(
"route" => array("A" => 1, "B" => 2),
"_serialize" => array("route")
));
return;
}
Here is a line from my routes.php file:
Router::parseExtensions();
Here is my test code:
$result = $this->testAction("/itinerary/calculate.json", array(
"method" => "POST",
"return" => "contents"
));
This code throws
MissingViewException: View file "C:\xampp\htdocs\fiver\app\View\Itinerary\calculate.ctp" is missing.
I am obviously missing something here. Please help. Another test for another controller with JSON works just fine
Got it. CakePHP requires the RequestHandler component to be explicitly added to the controller for the extensions to work. I've added this line, it started to work
public $components = array('RequestHandler');
If you dont have/want a view for your controller you can simply add
$this->autoRender = false;
// EDIT: Only working if you dont want an output but thats not the case

Ember CLI Controller Test: Uncaught TypeError: Cannot read property 'transitionToRoute' of null

I have a controller I'm testing with Ember CLI, but the controller's promise will not resolve, as the controller's transitionToRoute method is returning null:
Uncaught TypeError: Cannot read property 'transitionToRoute' of null
login.coffee
success: (response) ->
# ...
attemptedTransition = #get("attemptedTransition")
if attemptedTransition
attemptedTransition.retry()
#set "attemptedTransition", null
else
#transitionToRoute "dashboard"
login-test.coffee
`import {test, moduleFor} from "ember-qunit"`
moduleFor "controller:login", "LoginController", {
}
# Replace this with your real tests.
test "it exists", ->
controller = #subject()
ok controller
###
Test whether the authentication token is passed back in JSON response, with `token`
###
test "obtains authentication token", ->
expect 2
workingLogin = {
username: "user#pass.com",
password: "pass"
}
controller = #subject()
Ember.run(->
controller.setProperties({
username: "user#pass.com",
password: "pass"
})
controller.login().then(->
token = controller.get("token")
ok(controller.get("token") isnt null)
equal(controller.get("token").length, 64)
)
)
When the line #transitionToRoute("dashboard") is removed, the test passes; otherwise, the test fails.
How can I fix this error, while still maintaining my controller logic?
Work around: bypass transitionToRoute if target is null. Something like:
if (this.get('target')) {
this.transitionToRoute("dashboard");
}
I ran into the same error and dug into Ember source code a little bit. In my case this error is thrown by ControllerMixin because get(this, 'target') is null at this line. The test module probably has no idea what target should be in a controller unit test like this without further context, so you may need to manually set it or just bypass it.
Since you're not interested in the transition itself, you can just stub out the transitionToRoute method on the controller.
JS:
test('Name', function() {
var controller = this.subject();
controller.transitionToRoute = Ember.K;
...
}
Coffee:
test "it exists", ->
controller = #subject()
controller.transitionToRoute = Ember.K
ok controller
Not sure why transitionToRoute method is undefined when you execute it within an unit test - it is probably related to the fact that the execution context is different.
One possible workaround to this would be if you move your transitionToRoute call to the route instead of it being in the controller. That way your controller will send action to its route and you'll keep routing only in the route.
There is a big discussion around which is better practice - routing from controller or not but this is another story.