I have a controller which relies on a service built through ngResource. I am having trouble testing this(although both appear to work like a charm in the actual application). The following is the (sanitized) Controller
MyApp.Controller.MyCarsController = (scope, http, typeService) ->
if scope.context==undefined
scope.ferrari_or_porshe =""
scope.id = ""
else if scope.context=="ferrari"
scope.country_or_pi ="Ferrari"
else if scope.context=="porshe"
scope.country_or_pi ="Porshe"
typeService.index
ferrari_or_porshe: scope.ferrari_or_porshe
id: scope.id
, (response) ->
scope.type = response
scope.loading = false
MyApp.Controller.MyCarsController.$inject = ['$scope', '$http', 'Type']
And this is the Service:
MyApp.MyModule.factory 'Type', ['$resource', ($resource) ->
TypeResource = $resource("/api/types/:ferrari_or_porshe/:id", {},
index:
method: "GET"
isArray: true
)
return TypeResource
]
Finally, some test code:
describe 'MyApp.Controller.MyCarsController', ->
beforeEach module('MyModule')
beforeEach inject ($rootScope, $http, $controller, Type) ->
#scope = $rootScope.$new()
#typeService = Type
#scope.context = undefined
$controller 'MyApp.Controller.MyCarsController', $scope: #scope
describe '#home-page', ->
it 'contains a list of types', ->
expect(#scope.types.length).toBeGreaterThan 0
it "sets instance variables correctly", ->
expect(#scope.ferrari_or_porshe).toBe ""
expect(#scope.id).toBe ""
Which fails with:
No more request expected in helpers/angular-mocks.js on line 889
TypeError: 'undefined' is not an object (evaluating 'this.scope.types.length') in controllers/my_cars_controller_spec.js
By judicious application of console.logs, I have discovered that the issue is that the final callback on response is never reached. TypeResource comes back as [Function].
My questions is:
How do I drive the Jasmine Tests to correctly enter the Service and fetch a response? And is there any way to create direct Unit Tests for Services?
Any and all help is appreciated
The Solution is as follows: for the Service, use $httpBackend which is bundled as part of ngMock:
http://code.google.com/p/google-drive-sdk-samples/source/browse/ruby/app/lib/angular-1.0.0/angular-mocks-1.0.0.js?r=c43c943e32be395b7abca8150deb301d3cbc0dbe
Use this to mock the Rest responses. Since in my case I only cared about verifying that a GET request goes out:
describe 'Type', ->
describe '#index', ->
beforeEach module('MyModule')
beforeEach inject(($httpBackend) ->
$httpBackend.whenGET('/api/types/ferrari/1').respond([])
)
it 'for a ferrari scope', inject((Type) ->
ferrari_or_porsche = 'ferrari'
id = '1'
expect( Type.index('ferrari_or_porsche': ferrari_or_porsche, 'id': id) ).toEqual([ ])
)
And then for the controller, mock the service using jasmine spies and use jasmine.any(Function) to warn of the callback.
describe 'MyApp.Controller.MyCarsController', ->
beforeEach module('myModule')
beforeEach inject ($rootScope, $http, $controller, Type) ->
#scope = $rootScope.$new()
#typeService = Type
#scope.context = undefined
spyOn(#typeService, 'index')
describe '#home-page', ->
beforeEach inject ($controller) ->
$controller 'MyApp.Controller.MyCarsController', $scope: #scope
it 'contains a list of types', ->
expect(#typeService.index).toHaveBeenCalledWith({ ferrari_or_porsche : '', id : '' }, jasmine.any(Function))
it "sets instance variables correctly", ->
expect(#scope.ferrari_or_porsche).toBe ""
expect(#scope.id).toBe ""
Note: I make no claims as to the "canonicalness" of this solution. But it works.
Note: The API endpoints are of course tested extensively elsewhere.
Related
I want use a route to get the complete collection and, if available, a filtered collection.
so my route:
$app->get("/companies", \App\Handler\CompanyPageHandler::class, 'companies');
My Handler for this route:
use App\Entity\Company;
use App\Entity\ExposeableCollection;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
class CompanyPageHandler extends AbstractHandler
{
public function handle(ServerRequestInterface $request): ResponseInterface
{
$categories = new ExposeableCollection();
foreach (['test', 'test1', 'test3'] as $name) {
$category = new Company();
$category->setName($name);
$categories->addToCollection($category);
}
return $this->publish($categories);
}
}
When getting this route /companies, i get the expected collection
[{"name":"test"},{"name":"test1"},{"name":"test3"}]
So now i change the route
$app->get("/companies[/:search]", \App\Handler\CompanyPageHandler::class, 'companies');
It's all fine when i'm browsing to /companies.
But if i try the optional parameter /companies/test1 then i got an error
Cannot GET http://localhost:8080/companies/test1
my composer require section:
"require": {
"php": "^7.1",
"zendframework/zend-component-installer": "^2.1.1",
"zendframework/zend-config-aggregator": "^1.0",
"zendframework/zend-diactoros": "^1.7.1 || ^2.0",
"zendframework/zend-expressive": "^3.0.1",
"zendframework/zend-expressive-helpers": "^5.0",
"zendframework/zend-stdlib": "^3.1",
"zendframework/zend-servicemanager": "^3.3",
"zendframework/zend-expressive-fastroute": "^3.0"
},
In Zend Framework 2 and Symfony4 this route definition works fine. So im confused.
Why my optional parameter doesn't work?
That's because you are using https://github.com/nikic/FastRoute router and correct syntax would be:
$app->get("/companies[/{search}]", \App\Handler\CompanyPageHandler::class, 'companies');
or be more strict and validate search param something like this:
$app->get("/companies[/{search:[\w\d]+}]", \App\Handler\CompanyPageHandler::class, 'companies');
I am trying to use the following Grails Mail plugin: https://grails.org/plugin/mail
I've added the depedency in BuildConfig.groovy:
plugins {
//mail plugin
compile "org.grails.plugins:mail:1.0.7"
}
The I've configured it to use a specific email by adding the following code in Config.groovy:
grails {
mail {
host = "smtp.gmail.com"
port = 465
username = "-my email-"
password = "-my password-"
props = ["mail.smtp.auth":"true",
"mail.smtp.socketFactory.port":"465",
"mail.smtp.socketFactory.class":"javax.net.ssl.SSLSocketFactory",
"mail.smtp.socketFactory.fallback":"false"]
from = "no-reply#kunega.com"
}
}
I have a controller where I declare the mailService so it should be injectd as a bean:
#Secured("permitAll")
class RegisterController {
def mailService
def springSecurityService
#Transactional
def registerAccount(UserCommand userCommand) {
def model
if (springSecurityService.isLoggedIn()) {
model = [success: false, message: 'Log out to register a new account.']
response.status = 400
} else if (userCommand.validate()) {
User u = userCommand.createUser()
u.save(flush: true);
Role role = Role.findByAuthority("ROLE_USER")
UserRole.create u, role, true
def link = createLink(controller: 'register', action: 'activateAccount', params: [code: u.confirmCode])
mailService.sendMail {
async true
to 'kunega#mailinator.com'
html "Activate your account on Kunega"
}
model = [success: true, message: 'An activation link has been sent to your email.']
response.status = 201
} else {
model = [success: false, errors: userCommand.getErrors()]
response.status = 400
}
render model as JSON
}
}
I am trying to use the sendMail method it in the registerAccount method of the controller. However I get an error, which basically says that the mailService object is null. Here is the error message:
errors.GrailsExceptionResolver NullPointerException occurred when processing request: [POST] /Kunega/register/createAccount
Cannot invoke method $() on null object. Stacktrace follows:
java.lang.NullPointerException: Cannot invoke method $() on null object
at com.kunega.RegisterController$_$tt__registerAccount_closure2.doCall(RegisterController.groovy:32)
at grails.plugin.mail.MailService.sendMail(MailService.groovy:53)
at grails.plugin.mail.MailService.sendMail(MailService.groovy:59)
at com.kunega.RegisterController.$tt__registerAccount(RegisterController.groovy:29)
at grails.plugin.cache.web.filter.PageFragmentCachingFilter.doFilter(PageFragmentCachingFilter.java:198)
at grails.plugin.cache.web.filter.AbstractFilter.doFilter(AbstractFilter.java:63)
at grails.plugin.springsecurity.web.filter.GrailsAnonymousAuthenticationFilter.doFilter(GrailsAnonymousAuthenticationFilter.java:53)
at grails.plugin.springsecurity.web.authentication.RequestHolderAuthenticationFilter.doFilter(RequestHolderAuthenticationFilter.java:49)
at grails.plugin.springsecurity.web.authentication.logout.MutableLogoutFilter.doFilter(MutableLogoutFilter.java:82)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)
And there is another strange thing that I should mention. I'm using IntelliJ Ultimate Edition, and here is a curios thing:
If you notice inside the highlighted area with red, the IDE is showing that it can't recognize the arguments inside the closure that is passed to sendEmail.
I've never used this plugin before, so I just followed the steps in the docs, but apparently something is wrong. Thank you for your help.
In your code you have:
html "Activate your account on Kunega"
which I suppose should be either:
html "Activate your account on Kunega"
or
html "Activate your account on Kunega"
otherwise you call a method html with params "Activate your account on Kunega".
I get the following error:
Uncaught TypeError: Cannot call method 'push' of undefined
In the next code:
class classDemo
names : ['t1', 't2']
methodM1: () ->
# This works:
#names.push 't3'
console.log #names.toString()
#socket = io.connect()
#socket.on 'connect', () ->
# This raise the error:
#names.push 't4'
console.log #names.toString()
Does anyone know how to push into "names" inside the socket.on method? (How to push 't4' correctly?
Thanks
EDIT: The solution proposed by #Sven works for one level of chaining. It seems to fail for two chained calls. Please consider the following example:
methodM1: () ->
_this = #
#socket = io.connect() # connect with no args does auto-discovery
#socket.on 'connect', () ->
# This works:
_this.names.push 'inside connect'
console.log _this.names.toString()
#socket.emit 'getModels', (data) ->
# This does not work:
_this.names.push 'inside emit'
console.log _this.names.toString()
I tried to apply the same solution again inside connect and before emit (see below) but I get no output:
_this2 = _this
#socket.emit 'getModels', (data) ->
_this2.names.push "inside emit"
console.log _this2.names.toString()
Thanks.
your emit is never fired because emit sends data and requieres therefore a datastructure.
Please change your code like this
a) use the fat arrow
b) emit a data structure
methodM1: ->
#socket = io.connect()
#here use the fat arrow it does the '_this = # automatically'
#socket.on 'connect', =>
#names.push 'inside connect'
console.log _this.names.toString()
#socket.emit 'getModels', yo: "got your message"
The fat arrow always binds to the outer instance (see When does the "fat arrow" (=>) bind to "this" instance)
I am not sure (well, I am pretty sure but havent tried it) that you can send a closure over the wire.
I've the follwoing test. For some reason the #$e I've set in the before function is undefined in the test:
assert = buster.assert
buster.testCase 'BaseChart',
before: ->
#el = sinon.spy()
#$el = [#el]
console.log(#$el)
##[LOG] [function spy() {}]
'updates when the model changed': ->
console.log(#$el)
##[LOG] undefined
This is a wild guess but change:
before: -> to before: =>
And see if that helps.
I try to write some simple tests in Coffeescript and Jasmine.
# greet.coffee
greet = (message, person) ->
"#{message}, #{person}!"
and here my Jasmine Spec File:
# greetSpec.coffee
describe 'greet', ->
it 'should greet with message and name', ->
result = greet 'Hello', 'John'
expect(result).toBe 'Hello, John!'
When i start SpecRunner in Jasmine i get:
ReferenceError: greet is not defined
I guess it has something to do with the namespace autogenerated by coffeescript and therefor is the greet function not visible from the Spec file. How can i solve it?
Ok, could solve it with a simple global variable (not sure if that's a good way, though):
greet.coffee:
#greet = (message, person) ->
"#{message}, #{person}!"
greetSpec.coffee:
describe 'greet', ->
it 'should greet with message and name', ->
result = greet 'Hello', 'John'
expect(result).toBe 'Hello, John!'