I currently use jasmine with karma to unit test my Angular controllers. I use JSON fixtures with mock data rather than relying on my actual services to make API calls.
Separately I test my API endpoints using jasmine-node.
My question is, do I need to also test my services in order to have a solid test suite? I feel like it would be overkill as all that is in my services are very simple $resource GET methods, and testing $resource is obviously not something which is necessary.
Related
Can we consider sending a request to an endpoint a unit or an integration test?
import lib from 'testing-lib';
// ...
const { testClient, expect } = lib;
const response = testClient
.request(app)
.get('/test/endpoint/');
// ...
expect(response).fulfills.some.condition.ok
I have a feeling that this is an integration test because it will ascertain that every piece, that is in between the request going out and the response coming back, is working as expected. I need to know if my vague understanding is correct or if I am missing some details.
I'm gonna go with neither. It's a functional test.
Unit tests test units of code. Hence the name. A unit of code is typically a function, class or module of some sort.
Integration tests validate that our units of code work together as expected. But it's still just testing the code.
Functional tests test the actual software in a deployed state through exposed interfaces.
So, in Node, a unit test might be testing one of your JavaScript modules by itself and mock out the dependencies. An integration test would be testing that your modules work together and would mock out only the extreme edges of the system. And a functional test would test that a particular endpoint works over HTTP and wouldn't mock anything.
I'll add that I encourage sticking to DRY principles while writing your tests. If you have a unit test that validates a thing, you don't need to validate that in an integration test. Just validate that the units work together as expected. And the same with functional tests. Don't validate the integration, that the units work together. Validate that the endpoints map to the expected behavior.
Yeap this is more an integration test.
Unit tests are more like the functional business logic test. For an example after your route handler (controller) received the request and calls Service to handle the logic.
Test of that logic is a unit test.
Integration test is checking if data-flow works fine.
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.
I have a repeated set of API calls in an Angular controller.
The relevant data is of course the API URI, POST or GET, some header data, and JSON objects. The API call always returns a JSON object.
I started to use a Directive but that seems most relevant for data that is used with $scope (e. g. in the View). This data runs under the radar and might eventually generate data used in the view but most usually not and most usually not in a way that can be abstracted.
So: Does this mean I shouldn't use a directive? If I don't use a directive, would it be better to use a JS Object (seems more modular) or Function (seems more variable friendly) to handle this code?
Yes, I get that objects can contain functions, but functions can also contain callbacks so...looking for kind of a "best practices" here. In terms of modularity and flexibility.
You should create Angular service for that.
https://docs.angularjs.org/guide/services
Your service will contain a method, lets say "getResults" which will make an API call and return either data or a promise ($http). Then you can inject you service to your controller and use that method to get the data and assign it to $scope.
An Angular service is certainly preferred to a more general JavaScript one because it allows you to take greater advantage of Angular's scope and other such things. Between Angular's Factory, Service, and Providers, a Service is the most in line with what you're trying to do since a Factory is too basic and generally used to solve smaller problems while a Provider is used -- as it says in the Angular docs -- "only when you want to expose an API for application-wide configuration that must be made before the application starts." Which is not what you're trying to do.
As far as I know all mocks in SinonJS are partial mocks, witch lead to uncut dependencies in tests when doing TDD.
This is an example of testing a method that connects to database and performs an insert:
Fist test: Mock connection to database to check params.
Implement connection logic to pass test.
Second test: Stub connection to database and mock insert to database.
Implement insert logic to pass test.
Doing this the first test will do the insert logic.
I know that full instances can be stubbed, but once stubbed it cannot be mocked.
Is there any way to create a full mock or any alternative?
I'm building an AngularJS app, and I would it to be respectful or the best practices.
Thus, when I have to call an external $resource or $http request, I do it in an external Service/Factory.
My problem is I don't know what is the best way for retrieving returned value of the Service into the calling Controller.
Currently, what I do is the following :
The controller calls the Service
The Service does the $http call and check the promise.
If promise is successful, the Service $broadcast an event with the returned object.
The Controller catches that event and does operations.
It works great and allows me to use the same event after different requests (ex: In a discussion, after retrieving all messages and after posted myself a message, the same event "new messages to display" is called)
.
But I finally decided to set up testing processes in my application (better late than never), and I realize that I could do it wrong.
--> When unit-testing my Controllers I want to mock some of my Services for returning given values. But as my Controller calls Services which doesn't returns anything (they fire events instead) I think it will be pretty hard and not natural to check Services "return" values. (I can always check if the Service has been called, but not the result).
.
Your opinion, have I done misconception errors ? I consider getting the promises directly into Controllers when calling Services, is this a good (better?) way to do it ?
I aware that each application have its own logic, but I think in AngularJS the number of "working" logics (that preserves modularity, and testability) are particularly restricted to some set of choices and best practices.
Any suggestion would be appreciated.
Without seeing your code, I have my ideas:
Your controller could receive that promise and do what it needs to do. There is no real need to broadcast anything. Your service call $http and then return the promise. When $http promise is resolved, your controller will know and can do what you need to do.
Testing functions in a service that doesn't return anything is not a problem, that is what spyOn is. You can check that the function has been called and that should be enough for your controller. You don't actually care what happens in the service, you cares about your controller (in the test controller, of course).
Please don't use events for such kind of interaction. They make a mess inside of the code and reduce maintainability. Sometimes you can use them but it is fine only for certain operations and for some specific situations. Using promises is good approach.
Here you have three different options:
Just return the $http promise object and handle it inside of your controller
Make your own promise with $q service and place inside of it your own logic for handling the response from $http or direct returning result (it is more flexible approach than the first one)
Just pass to the services a function callback which you can call there inside of your service to pass the result back to controller.
But you definitely don't need to use events in these cases.
What is related to unit-testing you will configure $httpBackend service to mock your queries and in this case everything will work fine and you can test anything you need ($httpBackend). It means that you can inject your service and it works fine (as instead of real http call it will return the object configured as a response in the $httpBackend). So you can still test your controller without need to make anything complicated to your services.