Last active
July 12, 2024 16:08
-
-
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…
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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