In typescript, what is the best way to test a private method. While testing a controller in Angularjs, sometimes controller add a property (function) to $scope, and if I do not make the property public, then assign to the $scope, I may to be able to test it. Is there a recommended way.
export class MyCtrl{
constructor($scope){
$scope.addProp = (d:string) => {
this.addProp();
}
}
private addProp(){
//...
}
}
One solution is to rearchitect your code so that it is more testable. You could create an AddPropProvider that does the actual work (with implementation details more exposed for testing) and pass it in to the constructor for MyCtrl. That's the classic inversion of control strategy.
Another solution is maybe just make that function public after all? Private is ideal for hiding implementation details that could change, but maybe you know that function is always going to be there anyways.
Finally there's the hack. That function really exists on the instances of MyCtrl and the compiler only doesn't want you calling it because you said so.
var m = new MyCtrl();
(<any>m).addProp();
Now if you remove or modify the signature for addProp that code will still compile so you've lost type safety. In most code that would be bad. But this is unit test code so we're going to learn about the break early and easily so it's not horrible.
One solution is to not use the scope as the strong glue to ui and only use it as a container for the controller class instance.this also has other advantages http://www.youtube.com/watch?v=WdtVn_8K17E
Then you can simply test your controller as you would test any other JavaScript class and not worry and $scope in your tests.
While I agree with Jeffery Grajkowski's solutions proposed above, there is one more option you should consider. Since JavaScript (and, by extension, TypeScript) doesn't have "real" private types, make it public, but private by convention.
That is, have the method name start with an underscore or "unitTest", so anyone who sees it will recognize that it shouldn't be used in production code.
Related
I have an app with AngularJS and TypeScript
I want to know is there any case when I should use AngularJS factory instead of TypeScript Static Class
Nothing changes with ES6/TypeScript classes. They are just syntactic sugar for JavaScript constructor functions.
Good use cases for factory services can be figured out by the process of elimination.
Since service services are preferable for OOP-flavoured units:
class FooClass {
static $inject = [...];
constructor(...) {}
}
app.service('foo', FooClass);
And value services are preferable for singleton units that aren't constructed from classes and don't involve DI:
app.value('bar', 'bar');
app.value('Baz', BazClass);
factory services can be used for the rest of the cases. I.e. when a service needs DI and returned value cannot be easily constructed from a class - a function, a primitive, an object that is returned from function call:
app.factory('qux', ($q) => $q.all([...]));
We had same problem two years ago. The decision was to stay with angular system and only build angular services. Neither angular factory or typescript static. The reason was we could track how service has been created and shared. (by hard)
To answer your question, angular factory is an object which still need injection base on angular system. It is good if you would like keep tight with angular.
On the other hand typescript is more general. When you call a static function, it is just a function. Like you can import it anywhere else not angular, and then use it.
Something about javascript pattern:
A common pattern for creating objects in JavaScript is the module pattern.The pattern involves returning an object from the function that will be your public API.
There is a popular variation to the module pattern called the revealing module pattern. What happens with the revealing module pattern is things start explicitly private, and then choices are made about whether they become public.
While reading up on AngularJs providers, I noticed that different authors use different receipes to descrive what AngularJS providers are.
These different receipes for defining "provider" is causing great confusion.
I have laid down the two different methods that I see in this plnkr:
http://plnkr.co/edit/ZelQLtRiibH1NqetSTqe?p=info
Also, the same are given below:
app.provider('provider1', function(){
this.$get = function(){
return "value from provider1";
}
});
app.provider('provider2', function(){
return {
$get: function() {
return "value from provider2";
}
}
});
As you will notice, in "provider1", I pass what seems like a constructor function.
In "provider2", I simply return a object.
Could someone help clarify what's going on here ?
After spending two days sifting through AngularJS source code, I finally found the solution. This is not a easy one or a obvious one. One has to know the source code for the framework quiet well in order to know the reason.
While registering a provider, you have the option of simply giving a object with $get property or a constructor function with $this method. Angular will handle both differently.
The provider constructor is instantiated right when it is registered. Hence if it has any dependencies that have yet not been registered then this method will fail.
In case of provider objects, this is not a must as the $get is executed lazily.
These are not interchangeable methods and both have their own use cases.
Based on the documentation, you may either provide a provider instance directly (your question is not concerned by this) or provide a constructor for the provider (both your examples do this).
The result of both your examples is essentially the same because of how constructor functions operate in JavaScript (pay attention to the paragraph starting with "The object returned by the constructor function becomes the result of the whole new expression.").
In the first example, method(s) are assigned to the default instance object, which then becomes the instance, as the constructor doesn't return any value. In the second example, constructor returns an object containing the method(s), which then becomes the instance.
I have an Angular application. Its working good, but as my application is getting bigger I'm worried about the large number of dependencies that I have to inject in each controller.
for example
app.controller('viewapps',[
'$scope','Appfactory','Menu','$timeout','filterFilter','Notice', '$routeParams',
function($scope,Appfactory,Menu,$timeout,filterFilter,Notice,$routeParams) {
//controller code..
}])
I am sure that the list of dependencies are going to increase in future. Am I doing something wrong here? Is this the right approach? What is the best way to effectively handle this?
It's hard to be specific without an exact use case, or seeing the exact code in your controller, but it looks like your controller might be doing too much (or might end up doing too much as you add things later). 3 things you can do:
Delegate more of the logic to service(s) that are injected in.
Separate out into different controllers, so each only has (just about) 1 responsibility.
Separate out into directives, each with their own controllers and templates, and allow options to be passed in, and output given out, via attributes and the scope option of the directive. This is often my preferred option, as you end up building a suite of reusable components, each with a mini-API.
It is fine for directives to be used like this, at least in my opinion. They aren't just for handling raw Javascript events, or accessing the DOM directly.
I've been playing with the idea of bundling services based on controllers.
So in your example you'd refactor your; AppFactory, Menu, filterFilter and Notice services into a single service e.g. ViewAppsServices.
Then you'd use your services like ViewAppsServices.AppFactory.yourFunction().
As I see it that way you can at least shift your injections into another file cleaning your controller up a bit.
I think readability would suffer a bit since another developer would then have to look at bundles rather than the controller itself.
Here's a JSFiddle I put together to demonstrate how it would work; this is how I'd imagine yours would work.
.service('ViewAppsServices', ['AppFactory', 'Menu', 'filterFilter', 'Notice',
function (AppFactory, Menu, filterFilter, Notice) {
return {
AppFactory: AppFactory,
Menu: Menu,
filterFilter: filterFilter,
Notice: Notice
};
} ])
Try to move as much logic as possible to services, even just make controller methods act as "routing - passing through" methods . After time you will see it very usefull if you will want to use similar methods in other controllers/directives. Anyway, 7 injections is in my opinion not much :)
(edit: see the comment of Matt Way below)
Also, a tip - in newer versions of Angular you don't have to write this array, just:
app.controller('viewapps', function($scope,Appfactory,Menu, $timeout,filterFilter,Notice,$routeParams){
//controller code..
}])
My approach is to use $injector, when there are lots of dependencies:
app.controller('viewapps', ['$scope','$injector',function($scope,$injector){
var Appfactory = $injector.get('Appfactory');
var Menu = $injector.get('Menu');
//etc...
}]);
The advantages:
Code can be minified and obfuscated safely
You don't need to count the index of the dependency, when you declare dependency as a function's parameter
I have a very small number of functions that I need to use like this:
data-ng-disabled="_isEmpty(grid.view) || view != 'preview'"
What I did was:
$scope._isEmpty = _.isEmpty;
so that the _isEmpty is actually the lodash function.
Is this a reasonable thing to do? Also if so then where would be a good place for me
to code the above line? Should I code that in my appController. Also should I attach
the ._isEmpty to $scope or $rootscope? I have heard people talking about making a service and then injecting this. But for a few lines of code this seems overkill.
It all depends on where this code is required. If it is heavily reliant on, or required by a particular data object or view, then it most likely belongs in a controller. If inside a controller, $scope should be used by any value you want to reference in a view.
If however, you are writing generic functions used throughout your application, then they should be put in something like a service, and injected where required. Most of the time if you find yourself using $rootScope, the code should probably be in a service. Services aren't really overkill, as you can see below:
angular.module('myapp.services.something', [])
.factory('myService', [function () {
return {
myFunc: function (someArg) {
console.log(someArg);
}
};
}]);
You could put any number of generic helper functions for example in a service like this, and inject them into any controller that requires their use.
Though generally not recommended, I could see this kind of problem solved with putting the functions into the rootscope. Then putting the line $scope._isEmpty = _.isEmpty; in every controller would not be necessary anymore. A far better way for me would be to recode the utility functions into directives, even if involves some coding. Another way to solve the problem is to use services.
Simple question: what is the best way to ensure that a function is only being called from inside my app? I just want to make sure that nobody could just type in the url and run it.
I've thought about using Auth, but I really don't need username/password protection. Then I thought about private/protected functions, but I've read that CakePHP doesn't handle with that very well. And if I use private, as an example, I wouldn't be able to call the protected function from another controller class.
I think there must be a simply solution to this, but I can't think of one.
Thank you all in advance. :)
if you have functions that are been used in different controllers, it would be better if you create a component.
But.... if you really need to.. if you really want to.. you could set the access by adding an underscore at the beginning of your method's name:
class ProductsController extends AppController {
// a browser can "access" this method
function iHaveAView(){
}
// this method is used only by controllers
function _cantTouchMe(){
}
Good Luck
EDITED: (finally found the doc about this)
You can also change the visibility of controller methods in CakePHP by
prefixing controller method names with underscores. If a controller
method has been prefixed with an underscore, the method will not be
accessible directly from the web but is available for internal use.
source: Controller Conventions
I think it's simple don't put the function as an action of a controller. Create a component and use that when you need it.
That would make your function unavailable to the public but available within your app.
What do you think?