I want to be able to inject services into my controllers, so I had a look at http://symfony.com/doc/current/cookbook/controller/service.html and after some fiddling with the notation (could be a little more consistent but whatever) I have my WebTestCase using the service definition entry.
But the controller needs the container itself injected (and does indeed extend ContainerAware via the default framework controller), and the ControllerResolver in the FrameworkBundle does not do that.
Looking at the code (Symfony\Bundle\FrameworkBundle\Controller\ControllerResolver::createController()) this is not suprising:
protected function createController($controller)
{
if (false === strpos($controller, '::')) {
$count = substr_count($controller, ':');
if (2 == $count) {
// controller in the a:b:c notation then
$controller = $this->parser->parse($controller);
} elseif (1 == $count) {
// controller in the service:method notation
list($service, $method) = explode(':', $controller, 2);
return array($this->container->get($service), $method);
} else {
throw new \LogicException(sprintf('Unable to parse the controller name "%s".', $controller));
}
}
list($class, $method) = explode('::', $controller, 2);
if (!class_exists($class)) {
throw new \InvalidArgumentException(sprintf('Class "%s" does not exist.', $class));
}
$controller = new $class();
if ($controller instanceof ContainerAwareInterface) {
$controller->setContainer($this->container);
}
return array($controller, $method);
}
Apparently when using service:method notation,, it directly returns the controller from the container, not injecting the container itself.
Is this is a bug or am I missing something?
This is not a bug. It works as expected. This workflow generally "protects" the Controller as a Service concept. This way You need to look at Controller as a regular Service. In regular Service You inject everything what You need - if You need the controller itself - inject it explicitly.
To explain it more clearly, this "protection" I mentioned helps to avoid using service:method notation in one place and controller::method or bundle:controller:method in another.
So if not this "protection" it would be difficult to state that particular Controller is described as a Service or not as this would depend on which notation will be called first in Container build-up.
Related
I have to unfortunately write Unit Tests for a legacy Sitecore MVC code base where two distinct Sitecore Contexts are called. I understand this comes under Integration Testing but i don't have the option of educating my project Leads on that front. So i have chosen to use FakeDb for emulating Sitecore Instance and NSubstitute for substituting injected Dependencies (can't use any Profilier API Frameworks like MS Fakes, TypeMock etc because of Budget constraints). I am providing the code below:
Method to be UnitTested
public bool DubiousMethod()
{
// This HttpContext call is pain area 1. This gets resolved when i call it using ItemContextSwitcher in Unit Tests.
string currentUrl = HttpContext.Current.Request.RawUrl;
// This Sitecore Context call to Site Name is pain area 2. This gets resolved when Unit Tests are run under SiteContextSwitcher.
string siteName = Sitecore.Context.Site.Name;
return true/False;
}
Unit Test Method
[Fact]
public void DubiousMethodUT()
{
// create a fake site context
var fakeSite = new Sitecore.FakeDb.Sites.FakeSiteContext(
new Sitecore.Collections.StringDictionary
{
{ "name", "website" }, { "database", "web" }, { "rootPath", "/sitecore/content/home" },
{ "contentStartItem", "home"}, {"hostName","https://www.myorignalsiteurl.com"}
});
using (new Sitecore.Sites.SiteContextSwitcher(fakeSite))
{
//DubiousClassObject.DubiousMethod(home) // When Debugging after uncommenting this line i get correct value in **Sitecore.Context.Site.Name**
using (Sitecore.FakeDb.Db db = new Sitecore.FakeDb.Db
{
new Sitecore.FakeDb.DbItem("home") { { "Title", "Welcome!" } ,
new Sitecore.FakeDb.DbItem("blogs") }
})
{
Sitecore.Data.Items.Item home = db.GetItem("/sitecore/content/home");
//bool abc = confBlogUT.IsBlogItem(home);
using (new ContextItemSwitcher(home))
{
string siteName = Sitecore.Context.Site.Name;
var urlOptions = new Sitecore.Links.UrlOptions();
urlOptions.AlwaysIncludeServerUrl = true;
var pageUrl = Sitecore.Links.LinkManager.GetItemUrl(Sitecore.Context.Item, urlOptions);
HttpContext.Current = new HttpContext(new HttpRequest("", pageUrl.Substring(3), ""), new HttpResponse(new StringWriter()));
Assert.False(DubiousClassObject.DubiousMethod(home); //When Debugging after commenting above DubiousMethodCall i get correct value for **HttpContext.Current.Request.RawUrl**
}
}
}
}
As you can observe that when i try to call the method from FakSiteContext then i am getting the correct value for Sitecore.Context.Site.Name however my code breaks when HttpContext.Current.Request.RawUrl is invoked in the method. Opposite happens when i invoke the method from ContextItemSwitcher(FakeItem) context. So far i have not been able to find a way to merge both the Contexts (which i believe is impossible in Sitecore). Can anyone suggest if i run my Unit Tests in an overarching context where i am able to contrl fakeSite Variables as well as FakeItem context variables as well and by extensions any other Sitecore Context calls?
Any help would be appreciated.
I'd recommend to take a look at Unit testing in Sitecore article as it seem to be what you need.
In short - you'll need to do a few adjustments in your code to make it testable:
1) Replace static HttpContext with abstract HttpContextBase (impl. HttpContextWrapper) so that everything can be arranged - DubiousMethod gets an overload that accepts DubiousMethod(HttpContextBase httpContext).
2) As for Sitecore Context data - it has Sitecore.Caching.ItemsContext-bound semantics (as mentioned in the article), so you could cleanup the collection before/after each test to get a sort of isolation between tests.
Alternatively you could bake a similar wrapper for Sitecore.Context as ASP.NET team had done for HttpContext -> HttpContextBase & impl HttpContextWrapper.
As the question states, is there any downside in referencing the service directly in the template as such :
[disabled]="stateService.selectedClient == null || stateService.currentStep == 1"
In my opinion this doesn't seem like good practice and I'd much rather keep a "selectedClient" object in whatever component needs to use it. How can I get the state and store it into local variables, while observing the changes:
example: I want to move from step1 to step2 by changing "currentStep" in the "stateService", however I want the component that keeps "currentStep" ALSO as a local variable to reflect the change in the state?
Is it good practice to reference services in html templates in Angular
2?
I'd generally avoid it. It seems to bring more chaos than good.
Cons:
Coming from OOP background, this approach looks like it breaks the Law of Demeter, but more importantly,
It's no longer MVC, where your controller (Angular2's Component) acts like a mediator between the view and the services.
Like Ced said, what if a call to a service's member is costly and we need to refer to it multiple times in the view?
At the moment my editor of choice (VS Code) does not fully support Angular2 templates; referencing too many things outside of its own Component's scope in a template makes refactoring not fun anymore.
Pros:
Sometimes it looks more elegant (because it saves you 2 lines of code), but trust me, it's not.
How can I get the state and store it into local variables, while
observing the changes
Madhu Ranjan has a good answer to this. I'll just try to make it more complete here for your particular example:
In your StateService, define:
currentStep : Subject<number> = new Subject<number>();
selectedClient: Subject<Client> = new Subject<Client>();
changeStep(nextStep: number){
this.currentStep.next(nextStep);
}
selectClient(client: Client) {
this.selectedClient.next(client);
}
In your Component:
currentStep: number;
constructor(stateService : StateService){
stateService.currentStep.combineLatest(
stateService.selectedClient,
(currStep, client) => {
if (client == null) {
// I'm assuming you are not showing any step here, replace it with your logic
return -1;
}
return currStep;
})
.subscribe(val => {
this.currentStep = val;
});
}
You may try below,
stateService
currentStep : Subject<number> = new Subject<number>();
somestepChangeMethod(){
this.currentStep.next(<set step here to depending on your logic>);
}
component
// use this in template
currentStep: number;
constructor(stateService : stateServiceClass){
stateService.currentStep.subscribe(val => {
this.currentStep = val;
});
}
Hope this helps!!
It is probably not a good idea to expose your subject inside of your state service. Something like this would be better.
StateService
private currentStep: Subject<number> = new Subject<number>();
changeStep(value: number) {
this.currentStep.next(value);
}
get theCurrentStep(): Observable<number> {
this.currentStep.asObservable();
}
Component
currentStep: number;
constructor(private stateService: StateService) {
this.currentStep = this.stateService.theCurrentStep;
}
Template
[disabled]="(currentStep | async) == 1" // Not sure if this part would work
I am trying to write some unit tests to ensure my routes are not accidentally rewritten. I found already an answer to check whether a correct controller is assigned to particular route here.
However I would like to check as well that correct middlewares are assigned to route. I tried similar approach with
$tmp = new CorsService;
$corsMiddleware = Mockery::mock('Barryvdh\Cors\HandleCors[handle]', array($tmp))
->shouldReceive('handle')->once()
->andReturnUsing(function($request, Closure $next) {
return $next($request);
});
\App::instance('Barryvdh\Cors\HandleCors', $corsMiddleware);
For some reason the test is not picking this up. I am assuming that is because middleware instances are not stored using App::instance.
What am I doing wrong?
So I have found out there are 2 issues with above code
You can not chain ->shouldReceive directly with return value of Mockery::mock
there is missing \ from Closure
Working example:
$tmp = new CorsService;
$corsMiddleware = Mockery::mock('Barryvdh\Cors\HandleCors[handle]', array($tmp));
$corsMiddleware->shouldReceive('handle')->once()
->andReturnUsing(function($request, \Closure $next) {
return $next($request);
});
\App::instance('Barryvdh\Cors\HandleCors', $corsMiddleware);
Don't forget to to use ->getMock() at the end, if you are going to chain things like ->shouldReceive directly to your Mock object:
$corsMiddleware = Mockery::mock('Barryvdh\Cors\HandleCors[handle]', array($tmp))
->shouldReceive('handle')->once()
->andReturnUsing(function($request, Closure $next) {
return $next($request);
})
->getMock();
Try to get routes and check their middlewares
// Get Routes
foreach (Route::getRoutes() as $route) {
$middleware = $route->gatherMiddleware();
$name = $route->getName();
\Log::debug($name.'--');
\Log::debug($middleware);
}
Let's say I have a SessionManager instance which I want to be accessible in every Route extending my ProtectedRoute Mixin, is it possible to inject this dependency into a "group of routes" as I can reference a single Route instance?
So instead of:
App.inject('route:protected1', 'sessionManager', 'session_manager:main');
App.inject('route:protected2', 'sessionManager', 'session_manager:main');
....
I could do something like
App.inject('route:protectedmixin', 'sessionManager', session_manager:main);
You certainly can, but it might involve a bit of juggling. You could define any logic to decide what to inject and where if you want to rely on the default conventions you could manually find this objects and then use the fullname when injecting.
Another option would be to do it for each route, regardless of whether they include the Mixin or not. Inject doesn't need the full name, if you call `App.inject('route', ...) it would work by default.
If going with option one, it would look something like this. You basically need to find those routes implementing their mixins and then inject into all of those.
var guidForMixin = Ember.guidFor(App.YourMixin);
var routesToInjectInto = Ember.keys(App).filter(function (key) {
var route, mixins;
if (key.match(/Route$/))
route = App[key];
mixins = Ember.meta(route).mixins;
if (mixins) {
!!mixins[guidForMixin];
}
return false;
);
routesToInjectInto.each( function (key) {
var keyForInjection = Ember.decamelize(key);
App.inject('route:' + keyForInjection, 'sessionManager', 'session_manager:main');
});
Also I would suggest doing all of this inside an initializer, but that might be a minor consideration.
Ember.onload('Ember.Application', function(Application) {
Application.initializer {
name: "sessionManager"
initialize: function (container, application) {
// do the above here. Refer to app as the namespace instead of App.
// use the container instead of App.__container__ to register.
};
});
I'm sure this is a fairly common situation. I'm using the Spring Security Core plugin and want to create a domain model that has a Person limited to certain roles:
class Workgroup {
Person manager
...
static constraints = {
manager(validator: {mgr ->
// it feels like there should be a more elegant, groovy way of doing this.
def auths = mgr.getAuthorities();
def returny = false
auths.each {
if(it.authority == 'ROLE_MANAGER')
{
returny = true
}
}
return returny
})
}
}
This test fails like a mofo:
void testInvalidManager() {
def nick = new Person(username:'Nick')
def nonManagerRole = new Role(authority:'ROLE_EMPLOYEE')
UserRole.create(nick,nonManagerRole)
def awesome = new Workgroup(name:'mooCows', manager:nick)
mockForConstraintsTests(Workgroup, [awesome])
assertFalse awesome.validate()
assertEquals "validator", awesome.errors["manager"]
}
testInvalidManager Error No signature of method: users.UserRole.save() is applicable for argument types: (java.util.LinkedHashMap) values: [[flush:false, insert:true]] Possible solutions: wait(), any(), wait(long), use([Ljava.lang.Object;), isCase(java.lang.Object), each(groovy.lang.Closure)
groovy.lang.MissingMethodException: No signature of method: users.UserRole.save() is applicable for argument types: (java.util.LinkedHashMap) values: [[flush:false, insert:true]]
Possible solutions: wait(), any(), wait(long), use([Ljava.lang.Object;), isCase(java.lang.Object), each(groovy.lang.Closure)
at users.UserRole.create(UserRole.groovy:32)
at users.UserRole.create(UserRole.groovy)
at users.UserRole$create.call(Unknown Source)
at users.WorkgroupTests.testInvalidManager(WorkgroupTests.groovy:17)
Is this better covered in Integration than Unit Testing? Do I need to mock UserRole (if so, how?)? How are these types of tests normally done?
UserRole.create() calls save(), so you need to use mockDomain() instead of just mockForConstraintsTests().
But that's only if you're ok with testing the domain model with mocks, which I would never do. The mocking support in Grails should be used when testing Controllers or other classes that use domain classes but shouldn't be bothered with real persistence, creating a database (even in-memory), etc. By removing that dependency you're concentrating on the current tier, trusting that the other tier is already properly tested. But when you use mocking to test domain classes, you're really just testing the mocking framework. So I always use integration tests for domain classes so they run against a real database.
To answer the implicit question from your code example, I'd write the constraint as
static constraints = {
manager validator: { mgr ->
mgr.authorities.find { it.authority == 'ROLE_MANAGER' } != null
}
}
The issue with its bulk is that you're using each() when a regular for loop would be preferable since you can return from a for loop. Use each() only when you really want to invoke the closure on every instance. Here's one that's less groovy than the other one but uses a for loop:
static constraints = {
manager validator: { mgr ->
for (auth in mgr.getAuthorities()) {
if (it.authority == 'ROLE_MANAGER') {
return true
}
}
return false
}
}