BACKGROUND
I have an Angular project that has a web API we use for back-end web requests, but now we need to transition to a different API provider.
The API requests are all integrated into Angular service code files, and each service exists to fulfill a specific responsibility.
What myself and my fellow devs are tasked to do are create new, "Version 2" services, with the same method signatures and parameters, they must call the second new API.
Are goal is to create new services that have the same methods and signatures taking exactly the same parameters and yielding the same results. However, the API method signatures on the servers are often slightly different, and some of those results coming from the new API may need to be modified and massaged to fit the original format.
SETUP
In order to aid in this transition, we are wanting to create slightly less-traditional Jasmine unit tests.
For each service being transitioned, we intend to create a ...spec.ts
file that will execute the following, high-level requirements for testing and debugging.
- Initialize the test and using
TestBed.configureTestingModule
, setup appropriate providers for an old service and a new service. - For each method on the services returning results from the server, run an
it(...)
test. - Within the test, first call
MyService.apiRequest()
. Get the observable and get the result. - Call an
expect(...)
the old result set to be truthy for a sanity check. - Call
MyServiceV2.apiRequest()
. Get the observable and get the result. expect(...)
the new result set to be truthy.- Construct an
expect(...)
statement, double-checking that the results from the old API match the new API results.
To be clear, these are NOT tests that we intend to use in a CI/CD pipeline or to double-check out code. These are Jasmine tests that we are using to quickly inspect the result sets from the various APIs we have to work with.
PROBLEM
I have tried various means of setting up tests like this but I cannot seem to get the results back from our services, wait on the observables and compare the data. A lot of Jasmine documentation I've found expects you to fake results through mocks when working with observables. This cannot work in our use case.
In our case, our service calls are legit, and are a means of double-checking our data results from the two specific API endpoints.
For full disclosure, the application is currently stuck at Angular 8.2.14 and Jasmine 2.8.0. Listed below are the various attempts I've made to get this to work.
Attempt #1
it('should get data from MyService.getData(projectId)', ()=>{
//oldSvc and newSvc are references to the old and new services, initialized in beforeEach(...)
//Call to get the first round of data (old data.)
oldSvc.getData(projectId).subscribe((oldData)=>{
expect(oldData).toBeTruthy();
newSvc.getData(projectId, false).subscribe((newData)=>{
expect(newData).toBeTruthy();
expect(newData).toEqual(oldData);
});
});
});
This returns a Jasmine success but it seems to be that this is because "SPEC HAS NO EXPECTATION". I guess the test finishes before the observable subscribers get a chance to finish, much less call the expect(...)
methods to make any checks against the data. Ergo, it looks successful, but it's not really doing anything.
Attempt #2
Questions like this one recommend using the first()
method to wait and take the first result from each asynchronous request. I have nothing against this approach, but in spite of every effort I've made, I get errors that first()
does not exist. I've added references to first
(see here for an SO question with instruction on how to do this) but this does not fix the issue. Even after adding the import, first()
does not exist, I get build errors and I had to abandon this approach. I even tried to ignore the error with special TS flags, and instead of getting build errors, I get first() does not exist
related errors from within Jasmine. This was a non-starter.
Attempt #3
I found this article mentioning that you can wait on the result and use the jasmine.clock.tick(...)
method to wait results. By integrating the method taken from the article into your test file, you can create "synchronous" requests to your observables. Great! ...but this didn't work. My results are always undefined, proving that it in fact does not wait for the results, even with generous clock wait times.
Here is the meat of the article and a sample call to this method.
//Method
function awaitStream(stream$: Observable<any>, skipTime?: number) {
let response = null;
stream$.subscribe(data => {
response = data;
});
if (skipTime) {
/**
* use jasmine clock to artificially manipulate time-based web apis like setTimeout and setInterval
* we can easily refactor this and use async/await but that means that we will have to actually wait out the time needed for every delay/mock request
*/
jasmine.clock().tick(skipTime);
}
return response;
}
//Usage within an it(...) method
...
const oldData = awaitStream(oldSvc.getData(projectId), 10000);
//after calling the method, the result of the observable should be in oldData. It isn't.
...
QUESTION
These are the various attempts I've made to get asynchronous data from API calls in Jasmine for usage and testing. I can't get any of this to work under our current Angular/Jasmine version restrains. Does anyone have a straight-forward method for calling and waiting on data coming from a service returning Observables, and inspecting the results in Jasmine?
Thank you.