Skip to content

Instantly share code, notes, and snippets.

@kamranayub
Last active July 12, 2024 16:08
Show Gist options
  • Save kamranayub/9654d6581fbcf63cf481 to your computer and use it in GitHub Desktop.
Save kamranayub/9654d6581fbcf63cf481 to your computer and use it in GitHub Desktop.
Example using Ninject and Moq to create a TestingImpersonationContext. All it does is replace the bound dependency with a new impersonated mock and then replaces it once disposed. Relies on a testing context that is available to any test class (could also be a test base class). `UserRepository` is just an example of code that retrieves a user fr…
public class TestingContext {
private IKernel _kernel;
public TestingContext() {
// create initial Strict mock that can always be setup outside impersonation context
UserContext = new Mock<IUserContext>(MockBehavior.Strict);
// register dependencies
_kernel = CreateKernel();
// Ninject MVC dependency resolver or <insert your own DI here>
DependencyResolver.SetResolver(new NinjectDependencyResolver(_kernel));
}
public Mock<IUserContext> UserContext { get; set; }
public IUserRepository UserRepository {
get { return DependencyResolver.Resolve<IUserRepository>(); }
}
public IUserService UserService {
get { return DependencyResolver.Resolve<IUserService>(); }
}
private IKernel CreateKernel() {
var kernel = new StandardKernel();
// register dependencies
kernel.Bind<IUserContext>().ToConstant(UserContext.Object);
kernel.Bind<IUserRepository>().To<UserRepository>();
kernel.Bind<IUserService>().To<UserService>();
return kernel;
}
public TestingImpersonationContext Impersonate(string username) {
return new TestingImpersonationContext(username, UserContext, _kernel);
}
}
public class TestingImpersonationContext : IDisposable {
private Mock<IUserContext> _originalContext;
private IKernel _kernel;
public TestingImpersonationContext(string username, Mock<IUserContext> originalContext, IKernel kernel) {
_originalContext = originalContext;
_kernel = kernel;
// set up new Strict impersonated mock so all calls must be setup
var impersonatedContext = new Mock<IUserContext>(MockBehavior.Strict);
impersonatedContext.Setup(ctx => ctx.IsAuthenticated).Returns(true);
impersonatedContext.Setup(ctx => ctx.Username).Returns(username);
_kernel.Unbind<IUserContext>();
_kernel.Bind<IUserContext>().ToConstant(impersonatedContext.Object);
}
public void Dispose() {
// restore original context
_kernel.Unbind<IUserContext>();
_kernel.Bind<IUserContext>().ToConstant(_originalContext.Object);
}
}
public SomeTest {
private TestingContext _context;
public void Setup() {
// create/inject testing context
// typically managed by testing framework
// you could also just create a TestBase with all the code from TestingContext
_context = //...
// you can create an impersonated context before every test (and dispose)
// _impersonationCtx = _context.Impersonate("myuser")
}
public void Teardown() {
// assert strict mocks
_context.UserContext.VerifyAll();
}
// or just a block of code within a test (e.g. to test Anonymous vs. Logged-in logic)
public void AnonymousUserShouldNotHaveAccessToPublicUserProfile() {
// set up mock by default to be anonymous
_context.UserContext.Setup(ctx => ctx.IsAuthenticated).Returns(false);
// impersonate block of code as user
using (_context.Impersonate("myuser") {
// register as impersonated user
_context.UserService.SetProfileToPrivate();
}
// anonymous again
var accessRequest = _context.UserService.RequestAccessToProfile("myuser");
// anonymous user can't access profile
Assert.AreEqual(AccessRequest.Deny, accessRequest);
}
}
public class UserService : IUserService {
private readonly IUserContext _userContext;
private readonly IUserRepository _userRepository;
// depends on IUserContext for current user context
public UserService(IUserContext userContext, IUserRepository userRepository) {
_userContext = userContext;
_userRepository = userRepository;
}
public void SetProfileToPrivate() {
if (!_userContext.IsAuthenticated) {
throw new ApplicationException("You are not an authenticated user.");
}
// ...
}
public AccessRequest RequestAccessToProfile(string username) {
// deny anonymous access
if (!_userContext.IsAuthenticated) return AccessRequest.Deny;
// etc.
return AccessRequest.Allow;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment