temporarily override a binding

700 views
Skip to first unread message

fschwiet

unread,
Jul 15, 2010, 4:11:48 AM7/15/10
to ninject
I have this weird half-integration test setup that I'm having
trouble supporting. Basically I will run integration tests against a
live server, but then within that server process I temporarily inject
test doubles for services. So if I want to test user login page, I
can run a test hitting the web browser with WatiN, it visits the
website, but then the website allows login with the test account
because the MemberhsipProvider is actually a test double mocked to
allow the particular user.
I've been doing this with child containers in Windsor, but finding
certain scenarios don't work. I wonder if there is a way to achieve
this with Ninject. What I need is a way to override existing bindings
for some services, replacing them with temporary substitutes. When
I'm done I want to restore it to the way it was.
If I need to clone the original Kernel, add my test doubles, and
temporarily redirect all IoC requests to that cloned container, that
is ok.

These tests express what I want to do. Any ideas on how it could be
done?


public class WhenTemporaryOverrideIsUsed : StandardKernelContext
{
[Fact]
public void override_takes_priority()
{
kernel.Bind<IWeapon>().To<Sword>();

Mock<Shuriken> testWeapon = new Mock<Shuriken>();

kernel.Bind<IWeapon>().ToConstant(testWeapon.Object);

var weapon = kernel.Get<IWeapon>();

weapon.ShouldBe<Shuriken>(testWeapon.Object);

// fails with
// Ninject.ActivationException : Error activating IWeapon
// More than one matching bindings are available.
}

[Fact]
public void override_can_be_removed()
{
kernel.Bind<IWeapon>().To<Sword>();

Mock<Shuriken> testWeapon = new Mock<Shuriken>();

kernel.Bind<IWeapon>().ToConstant(testWeapon.Object);
kernel.Unbind<IWeapon>(); // this unbinds both, but I
only want to unbind the override

var weapon = kernel.Get<IWeapon>();

weapon.ShouldBeInstanceOf<Sword>();

// fails with
//
Ninject.Tests.Integration.StandardKernelTests.WhenTemporaryOverrideIsUsed.override_can_be_removed'
failed: Ninject.ActivationException : Error activating IWeapon
// No matching bindings are available, and the type is not
self-bindable.
}
}

Miguel Madero

unread,
Jul 15, 2010, 7:25:48 PM7/15/10
to nin...@googlegroups.com
Not sure if this would help, but might give you some ideas.
Have a look at BeingBlock, not sure if you could configure different blocks on the ActivationBlock that you get.
 In previous versions of Ninject we used to be able to create a new scoped kernel. I think that functionality was replaced with activation blocks.
 
 

 

--
You received this message because you are subscribed to the Google Groups "ninject" group.
To post to this group, send email to nin...@googlegroups.com.
To unsubscribe from this group, send email to ninject+u...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/ninject?hl=en.




--
Miguel A. Madero Reyes
www.miguelmadero.com (blog)
m...@miguelmadero.com

Urs Enzler

unread,
Jul 16, 2010, 2:05:19 AM7/16/10
to ninject
You could use kernel.Rebind<IWeapon>()... to change the binding in
your first unit test. And then again to change it back.

Cheers,
Urs

fschwiet

unread,
Jul 17, 2010, 2:09:46 AM7/17/10
to ninject
The problem with that is, I want to be able to restore the original
bindings, without tracking what those were.

I tried to capture my requirements as some tests... and I made those
tests pass. I wonder if someone could look at the implementation and
let me know of potential pitfalls to this approach? This is not for
production system, but rather dependency injection for half an
integration test.

http://github.com/fschwiet/ninject/commit/10adc26d561af5fd4e223a865f28f5ae981a9608

Regards,
fschwiet

Remo Gloor

unread,
Jul 19, 2010, 6:33:54 AM7/19/10
to ninject
Why don't you simply create a new kernel for each test in the setup of
your tests and rebind all dependencies that you want to replace by a
mock?

What we do for each integration tests is the following:
1. Call the bootstrapper method to setup the the kernel as it is used
in the productive system
2. Rebind any required dependency such as views and database access by
a mock
3. Normal Arrange, Action, Assert Tests

The kernel is thrown away before the next test. so there is no need to
revert it to is previous state.

fschwiet

unread,
Jul 19, 2010, 12:05:53 PM7/19/10
to ninject
The kernel is in another process, and that process is running for
all tests. For my test scenario, its an MVC application. For this
half-integration test I'll replace the data repository, but nothing
else. I then access the site via the web browser, automated via WatiN
(or I may make HTTP requests directly to test the web services). This
lets me verify that generated links on the page work, that the view is
rendering somewhat properly, and that routing is running as I expect.
The approach is slower then unit tests for sure, but other then that
the tests are really clean in that the setup has no extra
abstractions. (For the repository I use an actual store, and add the
users/data etc that would be needed via the existing repository API).

Sean Chambers

unread,
Jul 19, 2010, 12:44:32 PM7/19/10
to nin...@googlegroups.com
IMO, that is out of scope of what an integration test should be doing (at least in the context of ninject). What you are doing is much higher level as functional/smoke tests but you are still wanting to have a large degree of integration setup which isn't really possible with functional testing (again, in the context of ninject).

You should test each portion in an integration test with the kernel, then have a different set of tests that are invoked using watin/selenium with everything matching as close to production as possible. This gets rid of inconsistencies when you get to production.

I'm sure you could get what your trying to do to work, but I personally don't see he value and your needing to add constructs to ninject to expose seams that you shouldn't actually need.

Is there any reason why you are trying to swap out all this things in the kernel when invoking your watin tests? All of our selenium tests run against a SQL database, test web services and a test website if applicable.

Make sense?

Sean

fschwiet

unread,
Jul 19, 2010, 1:30:55 PM7/19/10
to ninject
I recognize its not traditional, and had misgivings when I started.
It just worked well enough that I just kept at it :).

The reason I want to do this is that it lets me test certain things
(routing, View rendering) before their dependencies are ready. To do
proper integration testing additional components need to be
implemented first, forcing me to implement the repository first. I
avoid implementing the repository first so its requirements can be
fleshed out.

This is for things where I haven't found a good way to test in
isolation. For instance testing routing in isolation feels very
unnatural to me (if you have routing tests that you like, I'd be happy
to look at them). Test setup is complicated in that I am specifying
things that have nothing to do with the layer I'm testing. Views seem
even harder to test... How do you verify a view has link to some other
controller's actions? This is the cleanest way I've found to test it.

Testing controllers in isolation is easy, verifying the right model
is passed to the view again is easy, but I find this half-integration
testing necessary to test what happens after that. I'd be happy to
consider other approaches.


On Jul 19, 9:44 am, Sean Chambers <schamber...@gmail.com> wrote:
> IMO, that is out of scope of what an integration test should be doing (at least in the context of ninject). What you are doing is much higher level as functional/smoke tests but you are still wanting to have a large degree of integration setup which isn't really possible with functional testing (again, in the context of ninject).
>
> You should test each portion in an integration test with the kernel, then have a different set of tests that are invoked using watin/selenium with everything matching as close to production as possible. This gets rid of inconsistencies when you get to production.
>
> I'm sure you could get what your trying to do to work, but I personally don't see he value and your needing to add constructs to ninject to expose seams that you shouldn't actually need.
>
> Is there any reason why you are trying to swap out all this things in the kernel when invoking your watin tests? All of our selenium tests run against a SQL database, test web services and a test website if applicable.  
>
> Make sense?
>
> Sean
>

TWischmeier

unread,
Jul 15, 2010, 12:35:35 PM7/15/10
to ninject
By default, Ninject always uses the earliest defined binding to
resolve a request and it even will throw an exception if there are
multiple bindings found for a unique request. I have similar
requirements (overriding bindings), and I solved it by inheriting from
StandardKernel and overriding the Resolve Method like this:

public override IEnumerable<object>
Resolve(Ninject.Activation.IRequest request)
{
if (request.IsUnique)
{
request.IsUnique = false;
return base.Resolve(request).Reverse().Take(1);
}
else
return base.Resolve(request).Reverse();
}

Two things matter:
- Reverse()-ing the Enumeration ensures you will always use the latest
binding to resolve the request
- Take(1)-ing when a unique request arrives ensures that no exception
is thrown when Ninject expects only 1 instance

TWischmeier

unread,
Jul 16, 2010, 7:10:19 AM7/16/10
to ninject
I solved that problem by inheriting from StandardKernel and overriding
Resolve() like this:

public override IEnumerable<object>
Resolve(Ninject.Activation.IRequest request)
{
if (request.IsUnique)
{
request.IsUnique = false;
return base.Resolve(request).Reverse().Take(1);
}
else
return base.Resolve(request).Reverse();
}

- Reverse()-ing always uses the latest binding instead of the first
defined
- Take(1)-ing ensures to not throw an exception when only one binding
is expected but you have defined (ie overridden) multiple matching
bindings

Rebind() is only partially useful, as it removes all bindings for a
type, which is problematic when you rely on .Named() to differentiate
some bindings.
Reply all
Reply to author
Forward
0 new messages