Mocking an api call for testing in Laravel - laravel-8

I have a controller that calls an api outside like this:
public function getUserData(Request $request)
{
// api connection is established using guzzle and into $client variable
$userData = $client->get($request->user_id);
// ... process data
}
then in my test I do this.
public function testUserData()
{
$userData = $this->post('user.data', [
'user_id' => 1
]);
$this->assertEquals($userData['user_id'], 1);
}
The test is working but I want to mock the api call so that it will not actually fire that call outside of the application.

You could do it like this:
Inject your HTTP client to the controller through the constructor
Swap the implementation of your client with a mocked one in your test
So, basically:
// Controller
protected $client;
public function __construct(MyHttpClient $client)
{
// Config your client
// Then set it to the class property
$this->client = $client;
}
public function getUserData()
{
$this->client->post-> ...
}
Then in your test:
public function testUserData()
{
$clientMock = \Mockery::mock(MyHttpClient::class);
// You specify here your assertions for the API call
$clientMock->shouldReceive('post')->with(...)->andReturn(...);
$this->app->instance(MyHttpClient::class, $clientMock);
$userData = $this->post('user.data', [
'user_id' => 1
]);
$this->assertEquals($userData['user_id'], 1);
}

In Laravel 9, there is a mocking implementation of Illuminate\Support\Facades\Http where you can mock responses that are sent to specific hosts/urls.
This is taken from the example in the documentation:
Http::fake([
// Stub a JSON response for GitHub endpoints...
'github.com/*' => Http::response(['foo' => 'bar'], 200, $headers),
// Stub a string response for Google endpoints...
'google.com/*' => Http::response('Hello World', 200, $headers),
]);
Once the fakes are stubbed, you can then run your test normally and it will detect any requests sent to those faked endpoints.
It should also be mentioned that you will have to use the Laravel Http client in your own external requests for this to work correctly.

Related

Piranha CMS middleware to capture requests to /uploads

When a request is made for an image|file it doesn't reach my middleware.
I can see that when UseCms is called it adds PiranhaStartupFilter
serviceBuilder.Services.AddTransient<IStartupFilter, PiranhaStartupFilter>();
Which adds UseStaticFiles() to the ApplicationBuilder.
public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
{
return builder =>
{
builder
.UseSecurityMiddleware()
.UseStaticFiles()
.UseMiddleware<RoutingMiddleware>()
.UseMiddleware<SitemapMiddleware>();
next(builder);
};
}
How could I overwrite this functionality so that my middleware is called for requests?
I'm expecting a call to /uploads/foo.jpg would be picked up in the InvokeAsync method of my middleware, registered like so:
app.UsePiranha(options =>
{
options.Builder.CustomImageTools();
});
At present only files & favicon requests reach the InvokeAsync method in my middleware.
As middleware in asp.net is executed in the order they are added into the pipeline it should be sufficient to add your interceptor before making any calls to Piranha.
You can add the middleware by adding a service above the call to UseCms() in startup.cs.
services.AddPiranha(options =>
{
options.CustomImageFilter();
options.UseCms();
}
public class CustomImageFilter : IStartupFilter
{
public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
{
return app =>
{
app.UseWhen(
context => some_condition,
appbuilder => appbuilder.CustomImageTools()
);
// Call the next configure method
next(app);
};
}
}

How to create a unit test with a mocked class that uses Testcafé's TestController?

I have a test that mocks a class using typemoq but i have an issue with one of its function.
In this function I call Testcafé's TestController to use a specific Role but it does not work as expected.
What can I do in order to have an instance of TestController that I can use outside a testcafé test?
Here is an example test:
import {IMock} from "typemoq/Api/IMock";
import {Mock} from "typemoq";
import {Role, t} from "testcafe";
import { expect } from "chai";
class TestClass {
public static async useRole(role: Role): Promise<boolean> {
await t.useRole(role);
return true;
}
}
describe("test mock using Testcafé's TestController", () => {
it('should switch role', async () => {
const role = Role.anonymous();
const mockedClass: IMock<typeof TestClass> = Mock.ofType<typeof TestClass>();
mockedClass.setup(x => x.useRole(role)).returns(async (role) => {
return TestClass.useRole(role);
});
const result = await mockedClass.object.useRole(role);
expect(result).to.eq(true);
});
});
When debugging you should get this:
Thanks in advance :)
You can use TestCafe functions only if a browser is opened and a TestCafe test is running, so they will not work in custom code.
The useRole method (and most of other methods) also works only when a browser is opened because the browser and server code interact inside this method. useRole is designed to get cookies and local storage from the client side. So, even if you were able to mock the useRole method, it would lose the most important part of its logic.

Http post and get request in angular 6

In angular 5.2.x for http get and post I had this code:
post(url: string, model: any): Observable<boolean> {
return this.http.post(url, model)
.map(response => response)
.do(data => console.log(url + ': ' + JSON.stringify(data)))
.catch(err => this.handleError(err));
}
get(url: string): Observable<any> {
return this.http.get(url)
.map(response => response)
.do(data =>
console.log(url + ': ' + JSON.stringify(data))
)
.catch((error: any) => Observable.throw(this.handleError(error)));
}
In angular 6 it doesn't work.
How can we make an HTTP post or get request?
Update :
In angular 7, they are the same as 6
In angular 6
the complete answer found in live example
/** POST: add a new hero to the database */
addHero (hero: Hero): Observable<Hero> {
return this.http.post<Hero>(this.heroesUrl, hero, httpOptions)
.pipe(
catchError(this.handleError('addHero', hero))
);
}
/** GET heroes from the server */
getHeroes (): Observable<Hero[]> {
return this.http.get<Hero[]>(this.heroesUrl)
.pipe(
catchError(this.handleError('getHeroes', []))
);
}
it's because of pipeable/lettable operators which now angular is able to use tree-shakable and remove unused imports and optimize the app
some rxjs functions are changed
do -> tap
catch -> catchError
switch -> switchAll
finally -> finalize
more in MIGRATION
and Import paths
For JavaScript developers, the general rule is as follows:
rxjs: Creation methods, types, schedulers and utilities
import { Observable, Subject, asapScheduler, pipe, of, from, interval, merge, fromEvent } from 'rxjs';
rxjs/operators: All pipeable operators:
import { map, filter, scan } from 'rxjs/operators';
rxjs/webSocket: The web socket subject implementation
import { webSocket } from 'rxjs/webSocket';
rxjs/ajax: The Rx ajax implementation
import { ajax } from 'rxjs/ajax';
rxjs/testing: The testing utilities
import { TestScheduler } from 'rxjs/testing';
and for backward compatability you can use rxjs-compat
You can do a post/get using a library which allows you to use HttpClient with strongly-typed callbacks.
The data and the error are available directly via these callbacks.
The library is called angular-extended-http-client.
angular-extended-http-client library on GitHub
angular-extended-http-client library on NPM
Very easy to use.
Traditional approach
In the traditional approach you return Observable<HttpResponse<T>> from Service API. This is tied to HttpResponse.
With this approach you have to use .subscribe(x => ...) in the rest of your code.
This creates a tight coupling between the http layer and the rest of your code.
Strongly-typed callback approach
You only deal with your Models in these strongly-typed callbacks.
Hence, The rest of your code only knows about your Models.
Sample usage
The strongly-typed callbacks are
Success:
IObservable<T>
IObservableHttpResponse
IObservableHttpCustomResponse<T>
Failure:
IObservableError<TError>
IObservableHttpError
IObservableHttpCustomError<TError>
Add package to your project and in your app module
import { HttpClientExtModule } from 'angular-extended-http-client';
and in the #NgModule imports
imports: [
.
.
.
HttpClientExtModule
],
Your Models
export class SearchModel {
code: string;
}
//Normal response returned by the API.
export class RacingResponse {
result: RacingItem[];
}
//Custom exception thrown by the API.
export class APIException {
className: string;
}
Your Service
In your Service, you just create params with these callback types.
Then, pass them on to the HttpClientExt's get method.
import { Injectable, Inject } from '#angular/core'
import { SearchModel, RacingResponse, APIException } from '../models/models'
import { HttpClientExt, IObservable, IObservableError, ResponseType, ErrorType } from 'angular-extended-http-client';
.
.
#Injectable()
export class RacingService {
//Inject HttpClientExt component.
constructor(private client: HttpClientExt, #Inject(APP_CONFIG) private config: AppConfig) {
}
//Declare params of type IObservable<T> and IObservableError<TError>.
//These are the success and failure callbacks.
//The success callback will return the response objects returned by the underlying HttpClient call.
//The failure callback will return the error objects returned by the underlying HttpClient call.
searchRaceInfo(model: SearchModel, success: IObservable<RacingResponse>, failure?: IObservableError<APIException>) {
let url = this.config.apiEndpoint;
this.client.post<SearchModel, RacingResponse>(url, model,
ResponseType.IObservable, success,
ErrorType.IObservableError, failure);
}
}
Your Component
In your Component, your Service is injected and the searchRaceInfo API called as shown below.
search() {
this.service.searchRaceInfo(this.searchModel, response => this.result = response.result,
error => this.errorMsg = error.className);
}
Both, response and error returned in the callbacks are strongly typed. Eg. response is type RacingResponse and error is APIException.
For reading full response in Angular you should add the observe option:
{ observe: 'response' }
return this.http.get(`${environment.serverUrl}/api/posts/${postId}/comments/?page=${page}&size=${size}`, { observe: 'response' });

Setting Headers (token) of multiple objects once user is authenticated in Angular2

Hi I've created a generic service from which I can create objects with generic http requests.
The challenge I'm facing now is to pass to each created object a token into their headers if the user is authenticated (i will get a token as response, which is stored in the localStorage)
So basically I can create these custom http objects anywhere (component-wise by injection) on any level. All of them don't have the Authentication Header set yet. Once User is authenticated, all these object to have their Authentication Header set.
Here's the plunker
export class App {
myHttpObject1;
constructor(private myAuth:MyAuth, private myDatabase:MyDatabase) {
this.name = 'Angular2 (Release Candidate!)'
this.myHttpObject1 = this.myDatabase.httpSchema('users')
this.myHttpObject1.log()
// this.myHttpObject1.someOtherMethodes()...
}
login(){
this.myAuth.login()
}
showHeaders(){
this.myHttpObject1.log()
}
}
Below is the Service and one to mock a login.
#Injectable()
export class MyDatabase{
private base_url:string;
private headers :Headers;
constructor(){
this.base_url = 'https://jsonplaceholder.typicode.com/';
this.headers = new Headers();
this.headers.append('Content-Type', 'application/json');
this.headers.append('Authorization','');
}
public httpSchema(path:string){
return new MyHttpObject(path, this.headers || new Headers())
}
}
class MyHttpObject{
constructor(public url:string, public headers:Headers){
}
log(){
console.log(this.url)
console.log(this.headers)
}
post(){
console.log('here could be a http post')
}
}
#Injectable()
export class MyAuth{
login(){
setTimeout(()=>{
console.log('logged In');
localStorage.setItem('token':'mytoken');
},2000)
}
}
Look at the Http Injector, it intercepts the call and adds whatever you need to the http object.

Can I set authorization headers with RequireJS?

We want to have 2 sets of resources for our AngularJS app (public/private) which uses RequireJS for dependency management. Basically everything on the login page would be public and once logged in, another angularjs app would be loaded (new requirejs config) that would load resources that require authentication to access.
Is there a way to configure requirejs to set an authorization header when loading resources?
It depends on what you mean by "resources" and how your server is configured. But in general - yes, since you are using AngularJS you can use the $httpProvider to inject an interceptor service.
For example, in a service:
var dependencies = ['$rootScope', 'userService'];
var service = function ($rootScope, userService) {
return {
request: function(config) {
var currentUser = userService.getCurrentUser();
var access_token = currentUser ? currentUser.access_token : null;
if(access_token) {
config.headers.authorization = access_token;
}
return config;
},
responseError: function (response) {
if(response.status === 401) {
$rootScope.$broadcast('unauthorized');
}
return response;
}
};
};
module.factory(name, dependencies.concat(service));
Then, after you configure your routes, you can use:
$httpProvider.interceptors.push( 'someService');
You can find some more information on interceptors here: https://docs.angularjs.org/api/ng/service/$http#interceptors
UPDATE
You might be able to use the text plugin to try and receive it, but I don't see the point in protecting client side code. Plus, if you want to use optimization the resources will just come in one file anyway...
config: {
text: {
onXhr: function (xhr, url) {
xhr.setRequestHeader('Authorization','Basic ' + token);
}
}
}
Refer to: custom-xhr-hooks
Another UPDATE
You could also use urlArgs (mainly used for cache invalidation) without using the text plugin:
require.config({
urlArgs: 'token='+token,
...
)}