How to mock an object injected in Controller while calling an endpoint in Laravel? - api

Example:
I have a Route file with an endpoint /something/cool:
$router->get('/something/cool', [
'uses' => 'MyController#myFunctionOne',
]);
And I have a Controller named MyController.
In MyController I have a function named myFunctionOne.
In the myFunctionOne parameter I have an injected service class named MyService.
MyService has a function that calls an external API callExternalApi().
Here's how my controller looks like:
class MyController
{
public function myFunctionOne(MyService $myService)
{
$myService->callExternalApi();
// do some other things..
}
}
On the other side I have a functional test:
class SomethingCoolTest extends TestCase
{
public function testSomethingCool()
{
// callin my Route Endpoint (real http call to my app)
$this->get('/something/cool', [])->response;
// do some assertions..
}
}
My question is: how can I mock the controller injected service, since it's calling an external service?

That was easier than I expected :D
First create a mocking helper function named mock:
public function mock($class)
{
$mock = \Mockery::mock($class);
$this->app->instance($class, $mock);
return $mock;
}
Then Mock any service you like, as follow:
$mimo = $this->mock(MyService::class);
$mimo->shouldReceive('callExternalApi')->once()->andReturn([
"anything" => "cool"
]);

Related

How to pass argument to Class-based Middleware

I have custom class-based middleware like:
#Service()
export class MyMiddleware implements MiddlewareInterface<Context> {
constructor(private readonly service: Service) {}
async use({ info, context }: ResolverData<Context>, next: NextFn) {
// this.service.doSomeDbLogicHere()
return next();
}
}
#UseMiddleware(MyMiddleware)
#Mutation(() => User)
public async createuser() {}
I wonder how I can pass custom static values to my middleware, but still have other objects injected via DI.
You need to create a function that accepts a static value and return a middleware class.

OnAuthorizationAsync not being called when creating custom AuthorizeFilter that inherits from AuthorizeFilter

I've created a custom authorize filter which looks like this:
public class BearerTokenAuthorizeFilter : AuthorizeFilter
{
public override async Task OnAuthorizationAsync(AuthorizationFilterContext context)
{
await base.OnAuthorizationAsync(context);
if (context.Result is ChallengeResult)
{
// Then return a problem detail
ObjectResult result = new ObjectResult(new ProblemDetails
{
Type = ProblemDetailsTypes.Unauthorized,
Title = ReasonPhrases.GetReasonPhrase(StatusCodes.Status401Unauthorized),
Status = StatusCodes.Status401Unauthorized,
Detail = ProblemDetailsDescriptions.Unauthorized
});
result.ContentTypes.Add(new MediaTypeHeaderValue(new Microsoft.Extensions.Primitives.StringSegment("application/problem+json")));
context.Result = result;
await context.HttpContext.ChallengeAsync();
}
else if (context.Result is ForbidResult)
{
context.Result = new StatusCodeResult(StatusCodes.Status403Forbidden);
await context.HttpContext.ForbidAsync();
}
}
}
I am registering this filter like this:
services.AddMvcCore(options =>
{
options.Filters.Add<BearerTokenAuthorizeFilter>();
});
I have set the default authentication to be 'Bearer':
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
I have added Authorize attribute on the controller. Whenever I send an unauthorized request to the endpoint my custom filter is never called and I have no idea why? My goal is to return problem details if the request is unauthorized to provide a little bit more information to the consumer than just the status code. Why is my filter not being called?
Try implement IAuthorizationFilter or IAsyncAuthorizationFilter instead of AuthorizeFilter. It work for me. Also I noticed that GetFilter(..) method returns AuthorizeFilter instance directly in AuthorizationApplicationModelProvider when filter class implements AuthorizeFilter. But when filter implements IAuthorizationFilter or IAsyncAuthorizationFilter this method being not called I think that is issue in ASP NET
I have ended up implementing my own IControllerModelConvention class which looks like this:
public class BearerTokenAuthorizeConvention : IControllerModelConvention
{
private AuthorizationPolicy _policy;
public BearerTokenAuthorizeConvention(AuthorizationPolicy policy)
{
_policy = policy;
}
public void Apply(ControllerModel controller)
{
if (controller.Filters.OfType<BearerTokenAuthorizeFilter>().FirstOrDefault() == null)
{
//default policy only used when there is no authorize filter in the controller
controller.Filters.Add(new BearerTokenAuthorizeFilter(_policy));
}
}
}
This will be executed once per controller. I then registered this convention like this:
// Configure application filters and conventions
services.Configure<MvcOptions>(options =>
{
AuthorizationPolicy defaultPolicy = new AuthorizationOptions().DefaultPolicy;
options.Conventions.Add(new BearerTokenAuthorizeConvention(defaultPolicy));
});
At this point every controller I have will be tagged with this custom filter which will call base implementation of AuthorizeFilter. The reason why I wanted to derive from AuthorizeFilter was because I wanted to call the default implementation of Authorize and then handle failed response on my own. I thought I could accomplish this very functionality and somehow still be able to only use Authorize attribute. This doesn't seem to be possible. Unless it is an I'm missing something?

How to send a message outside the controller?

I need to send information to the connected clients outside the HUB.
Here my class :
public static class Notification{
public static void SendMessage(){
//... Do some stuff
MyHub.Clients.All.SendAsync("sendInfo");
}
}
How to instantiate HUB?
As far as I know, you could use IHubContext service to send the service message outside the hub.
If you have register the service inside the ConfigureServices in startup.cs, then you could access an instance of IHubContext via dependency injection.
services.AddSignalR();
E.g Inject an instance of IHubContext in a controller.
public class HomeController : Controller
{
private readonly IHubContext<NotificationHub> _hubContext;
public HomeController(IHubContext<NotificationHub> hubContext)
{
_hubContext = hubContext;
}
}
Now, with access to an instance of IHubContext, you can call hub methods as if you were in the hub itself.
public async Task<IActionResult> Index()
{
await _hubContext.Clients.All.SendAsync("Notify", $"Home page loaded at: {DateTime.Now}");
return View();
}
More details ,you could refer to this article.
You do as #Brando Zhang posted above or just inject the Hub in to your controller or manager like:
Controller
private IHubContext<YourHub, IYourHub> YourHub
{
get
{
return this.HttpContext.RequestServices.GetRequiredService<IHubContext<YourHub, IYourHub>>();
}
}
Other
private IHubContext<YourHub, IYourHub> YourHub
{
get
{
return this.serviceProvider.GetRequiredService<IHubContext<YourHub, IYourHub>>();
}
}
PS: It is recomended to inject the HubContext and not the hub directly.

StructureMap, MVC and ObjectInstantiation

I am using StructureMap as DI with MVC 4. I am pushing certain objects in the constructor via StructureMap.
Following I have in the the bootstraper
public static void ConfigureDependencies()
{
ObjectFactory.Initialize(IE =>
{
IE.UseDefaultStructureMapConfigFile = true;
});
}
Controller Factory is as following
public class ControllerMyFactory : DefaultControllerFactory
{
protected override IController GetControllerInstance(System.Web.Routing.RequestContext requestContext, Type controllerType)
{
return ObjectFactory.GetInstance(controllerType) as IController;
}
}
Then I am plugging this in Global.asax
BootStrapper.ConfigureDependencies();
ControllerBuilder.Current.SetControllerFactory(new ControllerMyFactory());
Following is one of my Controller
public class SomeController : Controller
{
ISomeService service;
public SomeController(ISomeService service)
{
this.service = service;
}
}
Now my problem is object Instantiation, which are being passed in the constructor.
I used to construct this object like Following
ISomeService service = CommonGateway.GetChannnel<ISomeService>();
How do I plugin this with StructureMap? How do I change the way StructureMap will instantiate the objects?
Please let me know if I am not very clear?
Thanks,
A
You just need to configure StructureMap to know about your ISomeService and how to instantiate it like this:
ObjectFactory.Initialize(IE =>
{
IE.For<ISomeService>().Use(() => CommonGateway.GetChannel<ISomeService>() as ISomeService);
});
This will then call your factory method when instantiating your controller, because your controller is already being created by StructureMap.

yii - variable available to each controller

I am new to yii.
I am using more than 1 controller in my website and each controller has few actions.
I want to use some variables across each controller (Value of variable will be fixed, I need some constants for a formula). Whats the best place (standard way) to define those variables ?
Should I use session ? (as value is not going to change).
Not sure what you are using your vars for, but you can do it by defining them in your config main.php
'params'=>array(
'someVar1'=>'varValue1',
'someVar2' => 'varValue2',
),
Then you can access them in ANYWHERE by calling
Yii::app()->params['someVar1']
They will be available anywhere in your application.
Or you can extend all your controllers off of a base class and define your constants there
Base Controller:
class Controller extends CController {
const SOME_VAR = 'someValue';
}
Your controller:
class YourController1 extends Controller
{
public function actionIndex()
{
echo parent::SOME_VAR;
}
}
Your other controller:
class YourController2 extends Controller
{
public function actionLogin()
{
echo parent::SOME_VAR;
}
}