I'm using Angular, and we have a factory which does some initialization of restangular defaults. It's responsible for adding a number of hooks into restangular paths for later.
I want to test the logic of this factory, but the problem is that most of the hooks are set before the factory is returned, their done once at initialization. As such I don't know how to test the logic. I can't add a spy, because by the time I have a factory object it's already been initialized and it's too late to test that init logic.
I can test that specific paths received the expected hooks, but I feel like that not a proper unit level test. Since were adding a number of general helper methods to every one of our paths I would really like to test in isolation that each general helper function was added done, in a way that is not depending on any of the other hooks we set specific to individual paths.
Is there a good way to plug in to Angular and test the init logic that occurs before a factory is returned? I would like to either modify a default array (the one that associates paths to specific hooks), or to be able to call helper functions in isolation.
Related
There seems to be several ways to stub out services when testing Angular controllers using Jasmine.
One of the ways I've become accustomed to is to do the following in a beforeEach block:
mockService = {}
inject( $controller) ->
controller = $controller('MyController', {
MyRealService: mockService
})
Another way is to use $provide to stub my dependency injected service:
module('app', ($provide) ->
mockService = {}
$provide.value('MyService', mockService)
)
When I had:
afterEach ->
httpBackend.verifyNoOutstandingExpectation()
in my test. Only the $provide method worked, and the $controller style would not. Using $controller the test was somehow hitting MyRealService and including all of its dependencies, rather than ignoring and using mockService. Without the verifyNoOutstandingExpectation(), both methods seem to behave the same and the test passes.
What are the main differences between the 2 styles? When should you be using one over the other? Any ideas why the effect of stubbing is different in the presence of a verifyNoOutstandingExpectation()
What are the main differences between the two styles?
In the first case, we inject our fake objects ourselves into the controller, whereas with the second method we tell Angular where it can find the fakes when it needs to inject them later.
When should you be using one over the other?
I prefer the first method as it is more obvious, and especially when we test controllers, it is sufficient.
I mostly use the $provide method when dealing with services/factories or routing testing. Since services can't be "newed up" like controllers, we need to trick the inject function to use our fakes using the provider.
And in the case of testing routing we have no other possibility than using the provider to stub out the services used in the route resolvers.
Any ideas why the effect of stubbing is different in the presence of a verifyNoOutstandingExpectation()
Not really sure on how to answer this one, but I have found $httpBackend to be not-so-obvious to use. Especially when testing routes. It records all traffic, including things like calls to templates defined in a route etc, which makes it very easy to overlook a call in the setup.
With Jasmine it is possible to spyOn methods, but I'm not clear on when would it be actually useful. My understanding is that unit tests should not be concerned with implementation details and testing if method is called would be implementation detail.
One place I might think of is spying on scope.$broadcast (Angular) etc but then again this would be implementation detail and not sure if unit tests should even bother with how the code works, as long as it gives expected result.
Obviously there are good reasons to use spyOn so what would be good place to use it?
The spyOn you describe is more commonly known in testing as a MOCK, although to be more clear it allows for 2 operations:
Create a new implementation for a method via createSpy (this is the classical mock)
Instrument an existing method via spyOn (this allows you to see if the method was called and with what args, the return value etc.)
Mocking in probably the most used technique in unit testing. When you are testing a unit of code, you'll often find that there are dependencies to other units of code, and those dependencies have their own dependencies etc. If you try to test everything you'll end up with an module / UI test, which are expensive and difficult to maintain (they are still valuable, but you want as few of those as possible)
This is where mocking comes in. Imagine your unit calls to a REST service for some data. You don't want to take a dependency on a service in your unit test. So you mock the method that calls the service and you provide your own implementation that simply returns some data. Want to check that your unit handles REST errors? Have your mock return an error. etc.
It can sometimes be useful to know if your code actually calls another unit of code - imagine that you want to make sure your code correctly calls a logging module. Just mock (spyOn) that logging module and assert that it was called X number of times with the proper parameters.
You can spy functions and then you will be able to assert a couple of things
about it. You can check if it was called, what parameters it had, if it
returned something or even how many times it was called!
Spies are highly useful when writing tests, so I am going to explain how to
use the most common of them here.
// This is our SUT (Subject under test)
function Post(rest) {
this.rest = rest;
rest.init();
}
We have here our SUT which is a Post constructor. It uses a RestService to
fetch its stuff. Our Post will delegate all the Rest work to the RestService
which will be initialized when we create a new Post object. Let’s start testing
it step by step:
`describe('Posts', function() {
var rest, post;
beforeEach(function() {
rest = new RestService();
post = new Post(rest);
});
});`
Nothing new here. Since we are going to need both instances in every test,
we put the initialization on a beforeEach so we will have a new instance every
time.
Upon Post creation, we initialize the RestService. We want to test that, how
can we do that?:
`it('will initialize the rest service upon creation', function() {
spyOn(rest, 'init');
post = new Post(rest);
expect(rest.init).toHaveBeenCalled();
});`
We want to make sure that init on rest is being called when we create a new
Post object. For that we use the jasmine spyOn function. The first parameter is
the object we want to put the spy and the second parameter is a string which
represent the function to spy. In this case we want to spy the function init
on the spy object. Then we just need to create a new Post object that will call
that init function. The final part is to assert that rest.init have been
called. Easy right? Something important here is that the when you spy a
function, the real function is never called. So here rest.init doesn’t actually
run.
Closure library offers a basic life cycle for Components:
Instantiation
Rendering/Decoration
Document Entering
Document Exiting
Disposal
I'm focusing on the first two. About design patterns, when is it better to instantiate a nested component in the first steps?
Instantiation: needs to be hold on a property till added through addChild and consumes memory before necessary. Anyways, it allows to do some dependency injection or better initialization because of the parameters it receives.
Rendering/Decorating: messes up the dom creation, which can be already complicated because of the references of other objects it needs. It also would need the instantiation parameters previously stored in some property. Anyways, holds the instantiation till is needed.
Maybe a separated method called after instantiation which wraps the rendering? I'm asking because Closure Libray book and documentation don't talk about this.
Doing some refactorying and trying to split logic, came to the following conclusion:
The best option I've found so far is to create the components inside the createDom method:
Normally, the parameters needed for components involve the data they present. My arquitecture uses DAO, which means all data objects are conveniently connected. Subcomponents usually need some other data objects which are accessible by the parent's DAO. Since the parent object is needing this DAO, it's ok to store it on a property for the use inside the createDom.
The other thing is that instantiation and rendering in the createDom component, theoretically, only needs two lines, which isn't a mess.
Again, it's the best possible solution to increase cohesion.
Note: I'm not seeking the differences between the value, factory, service, and provider 'recipes', as explained here.
I'm looking for clarification on the different ways to define them: Correct me if I'm wrong, but it appears as though
myApp = angular.module('myApp', [])
.value(...)
.factory(...)
.service(...)
.provider(...)
map to
$provide.value()
$provide.factory()
$provide.service()
$provide.provider()
And you can use either way. I asked this question, and realize I can use $provide as a dependency to module().config(). My questions:
When/why would I use angular.module().provider() vs using the $provide dependency?
Is there any way (or reason) to access/change a provider after definition?
Using AngularJS Batarang for Chrome, I'm looking at the various angular $scope properties, and don't see $provide or $injector. Where do these live?
The provider methods off the module definition are just short cuts. Use them as often as you like because it leads to shorter, easier to read and understand code. Less ritual/ceremony is involved than injecting the $provider service and calling that directly. The main reason to use $provide directly is to access a method on it that is not a short cut from module (such as the decorator) or if you have to do something from within a service or component that is not up at the module definition level.
The common case for changing a provider after it's definition is when you are integrating a third-party component and want to add or change the behavior. The third-party module will define the service and then you step in and override or extend it in some way that is specific to your app. A common case for example is to take the built-in Angular exception handler and extend that to interface with your own components.
$scope is a special "glue" used for data-binding and only exposes properties/functions that you explicitly set on the $scope. All of the other miscellaneous modules/services are stored within Angular's dependency injection container. The very first thing Angular does is create an $injector instance to keep track of dependencies. Therefore $injector === $injector.get('$injector'). Same with $provide. Anything prefixed with a $ is by convention a service that Angular places in the $injector for you to use.
If I have a popover say, and I want to test it:
being created
being manipulated
being destroyed
It is beneficial to have it declared in one place (tucked inside "describe"), so that it can be shared across the "its".
Should one share things between tests? I.E test2 relies on test1 being run first?
What is the best way to do this with Jasmine?
It`s a bad thing to rely to test order. To share things between tests you can have a way to set state of an object. Assume the pseudo code below:
var popover = getPopover({state:'init'});
//checking init state
...
//other test starting
var popover = getPopover({state:'manipulated'});
//checking the state
So the main idea is to be able to init your object at the state you need.
Note, that if it's not much code to perform initialization and you are not going to need to reuse it much, you can hardcode the state setup for every test. Sure, it's not dry, but you benefit from tests that can be read, without references to other methods. Sometime it's a good thing, but it depends.
Also, you can use beforeEach and afterEach for setup and teardown before and after every test( it's a describe level thing ). Which is one of the preferred ways to perform state initialization and cleanup.