I am trying to unit test this controller method, which comes out of the box in current MVC projects.
[AllowAnonymous]
public async Task<ActionResult> ConfirmEmail(string userId, string code)
{
if (userId == null || code == null)
{
return View("Error");
}
var result = await UserManager.ConfirmEmailAsync(userId, code);
return View(result.Succeeded ? "ConfirmEmail" : "Error");
}
The AccountController has a constructor which will take an ApplicationUserManager and a ApplicationSignInManager as parameters, and the matching properties with private setters to use for testing. However, I can't figure out how to mock out the ConfirmEmailAsync method.
You can mock various interfaces in the Identity namespace:
var store = new Mock<IUserStore<ApplicationUser>>();
store.As<IUserEmailStore<ApplicationUser>>()
.Setup(x => x.FindByIdAsync("username1"))
.ReturnsAsync((ApplicationUser)null);
var mockManager = new ApplicationUserManager(store.Object);
AccountController ac = new AccountController(mockManager, null, GetMockRepository().Object, GetMockLogger().Object);
But I can't find or figure out which Interface I need in order to create a mock of ConfirmEmailAsync.
How do I go about this? And for reference, is there a good way of finding out which interfaces these methods are on in order to mock and test them?
ConfirmEmailAsync is not currently part of an interface in the framework. It's in the UserManager<TUser, TKey> class which is the base class of Identity framework.
My solution?
Abstract all the things
I got around this by abstracting most of the functionality of identity into its own project so that I can unit test it easier and reuse the abstraction in other projects. I got the idea after reading this article
Persistence-Ignorant ASP.NET Identity with Patterns
I then fine tuned the idea to suit my needs. I basically just swapped everything i needed from asp.net.identity for my custom interfaces which more or less mirrored the functionality provided by the framework but with the advantage of easier mockability.
IIdentityUser
/// <summary>
/// Minimal interface for a user with an id of type <seealso cref="System.String"/>
/// </summary>
public interface IIdentityUser : IIdentityUser<string> { }
/// <summary>
/// Minimal interface for a user
/// </summary>
public interface IIdentityUser<TKey>
where TKey : System.IEquatable<TKey> {
TKey Id { get; set; }
string UserName { get; set; }
string Email { get; set; }
bool EmailConfirmed { get; set; }
string EmailConfirmationToken { get; set; }
string ResetPasswordToken { get; set; }
string PasswordHash { get; set; }
}
IIdentityManager
/// <summary>
/// Exposes user related api which will automatically save changes to the UserStore
/// </summary>
public interface IIdentityManager : IIdentityManager<IIdentityUser> { }
/// <summary>
/// Exposes user related api which will automatically save changes to the UserStore
/// </summary>
public interface IIdentityManager<TUser> : IIdentityManager<TUser, string>
where TUser : class, IIdentityUser<string> { }
/// <summary>
/// Exposes user related api which will automatically save changes to the UserStore
/// </summary>
public interface IIdentityManager<TUser, TKey> : IDisposable
where TUser : class, IIdentityUser<TKey>
where TKey : System.IEquatable<TKey> {
Task<IIdentityResult> AddPasswordAsync(TKey userid, string password);
Task<IIdentityResult> ChangePasswordAsync(TKey userid, string currentPassword, string newPassword);
Task<IIdentityResult> ConfirmEmailAsync(TKey userId, string token);
//...other code removed for brevity
}
IIdentityResult
/// <summary>
/// Represents the minimal result of an identity operation
/// </summary>
public interface IIdentityResult : System.Collections.Generic.IEnumerable<string> {
bool Succeeded { get; }
}
In my default implementation of the identity manager i simply wrapped the ApplicationManager and then mapped results and functionality between my types and the asp.net.identity types.
public class DefaultUserManager : IIdentityManager {
private ApplicationUserManager innerManager;
public DefaultUserManager() {
this.innerManager = ApplicationUserManager.Instance;
}
//..other code removed for brevity
public async Task<IIdentityResult> ConfirmEmailAsync(string userId, string token) {
var result = await innerManager.ConfirmEmailAsync(userId, token);
return result.AsIIdentityResult();
}
//...other code removed for brevity
}
Disclaimer: I work at Typemock.
Actually you don't need any interface if you are using Typemock, you just need to fake the IdentityResult you require and change the behavior of the asynchronous method "ConfirmEmailAsync", for example a test that checks the scenario of an Unconfirmed email:
[TestMethod, Isolated]
public async Task TestWhenEmailIsBad_ErrorMessageIsShown()
{
// Arrange
// Create the wanted controller for testing and fake IdentityResult
var controller = new aspdotNetExample.Controllers.AccountController();
var fakeIdentityRes = Isolate.Fake.Instance<IdentityResult>();
// Fake HttpContext to return a fake ApplicationSignInManager
var fakeSIM = Isolate.WhenCalled(() => controller.UserManager).ReturnRecursiveFake();
// Modifying the behavior of ConfirmEmailAsync to return fakeIdentityRes
Isolate.WhenCalled(() => fakeSIM.ConfirmEmailAsync("", "")).WillReturn(Task.FromResult<IdentityResult>(fakeIdentityRes));
Isolate.WhenCalled(() => fakeIdentityRes.Succeeded).WillReturn(false);
// Act
var result = await controller.ConfirmEmail("", "") as ViewResult;
// Assert
Assert.AreEqual("Error", result.ViewName);
}
Related
I'm working on an Asp.Net Core project targeted .Net 5 with Microsoft Identity and Entity framework core (Code first approache).
In my project some entities will inherit from IAuditProperties interface.
IAuditProperties:
This interface used to read/write some audition info from/in any Entity that implement it.
string CreatedBy { get; set; }
DateTime CreatedOn { get; set; }
bool IsEdited { get; set; }
string LastEditor { get; set; }
DateTime LastEditDate { get; set; }
In my project I wrote some extension methods that will write some auditon infos, all those extensions for any Entity that implemented the IAuditProperties interface.
WriteCreationAudit extension method as example
/// <summary>
/// Write audit properties for an <see cref="IAuditProperties"/> for the first creation
/// </summary>
/// <param name="obj"><see cref="IAuditProperties"/> object to write in</param>
/// <param name="appUser">Current user</param>
public static void WriteCreationAudit( this IAuditProperties obj,AppUser appUser)
{
obj.CreatedBy = appUser.FullName;
obj.CreatedOn = DateTime.Now.InMorocco();
obj.IsEdited = false;
}
What is exactly the core issue?
As you notice that the extention method WriteCreationAudit is recieves a appUser parameter, this parameter's type (AppUser) inherit from IdentityUser.
So, the exact issue is How can I create object from AppUser without pass it as parameter from the Controller ?
How I handle this issue at this time?
At this time I'm depending on Controllers and DI to get AppUser object and pass it to WriteCreationAudit method, and I don't love this technique.
So please, How can I achieve my goal about creating new object from AppUser from the extension method ? or if I can't achieve it is there any other good way ?
Massive thanks in advance.
Depend on the circumstance, I would suggest 2 approaching ways, then take whichever that suit your case most... or even better, take the idea and implement it your way.
Simple data was required
As your purposed, I saw every thing was required just a FullName and might be in the future userId. So, why just not simply put them somewhere in Jwt or even cookie depend on your authentication mechanism ? They're not such as ultra-secure information to guard. We can easily saw them here, even Jwt was designed to hold that kind of information. So, just inject IHttpContextAccessor into DbContext or repository if we make use of Repository pattern, take out User Info, then tweak a bit on the SaveChanges things.
Data required to process was some kiind complex or need to be secured.
Make something like BaseInfoRequest object that contain all the infomations we need, set them on some upper middleware and store in cache, with absolute expiration that equivalent to request timeout, the key should be HttpContext.Session.Id + "some constants string" that represent request infoObject. Then take them out from the cache wherever we need.
Just a small note: If we doesn't expose the UserName for example, but userId only, which mean foreach request we need to take UserName from somewhere. That's not a good idea in production scenarios. Take some consider about them to balance things out.
What's wrong with DI inject to controller then pass the param to the extension method?
I just recalled a while back Microsoft said don't inject SignInManager and UserManager in razor component (also not razor page/the razor component with #page). Instead, extend UserClaimsPrincipalFactory to add claims like:
public class AdditionalUserClaimsPrincipalFactory
: UserClaimsPrincipalFactory<AppUser, IdentityRole>
{
public AdditionalUserClaimsPrincipalFactory(
UserManager<AppUser> userManager,
RoleManager<IdentityRole> roleManager,
IOptions<IdentityOptions> optionsAccessor)
: base(userManager, roleManager, optionsAccessor)
{ }
public async override Task<ClaimsPrincipal> CreateAsync(AppUser user)
{
var principal = await base.CreateAsync(user);
var identity = (ClaimsIdentity)principal.Identity;
var claims = new List<Claim>();
claims.Add(new Claim("FullName", user.FullName?? ""));
identity.AddClaims(claims);
return principal;
}
}
I agree with #Gordon Khanh Ng. and this is just implementation difference
This is a very common behaviour and there are many ways to achieve this. Here is how you can do this. This is probably the easiest way
Override your SaveChanges()/ SaveChangesAsync() method in DbContext class. Also inject the IHttpContextAccessor in the constructor.
Then use this code inside your DbContext class.
The GetCurrentUserId() method may differ depending on your Identity implementation.
private string GetCurrentUserId()
{
var httpContext = _httpContextAccessor?.HttpContext ?? null;
if (httpContext.HasValue() && httpContext.User.HasValue())
{
var authenticatedUsername = httpContext.User.Claims.Where(c => c.Type == "sub")
.Select(c => c.Value).SingleOrDefault();
return authenticatedUsername;
}
return null;
}
public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = new CancellationToken())
{
ChangeTracker.DetectChanges();
var entries = ChangeTracker.Entries()
.Where(e => e.State != EntityState.Detached && e.State != EntityState.Unchanged);
foreach (var entry in entries)
{
if (entry.Entity is IAuditProperties trackable)
{
var now = DateTime.UtcNow;
var user = GetCurrentUserId();
switch (entry.State)
{
case EntityState.Added:
trackable.CreatedAt = now;
trackable.CreatedBy = user;
trackable.IsEdited = false;
break;
}
}
}
return base.SaveChangesAsync(cancellationToken);
}
I need to initialize an injected class at the scope level in ASP.NET Core - the initialization involves asynchronous method calls. You wouldn't do this in the constructor, nor a property accessor.
A common DI use in an asp.net core application is getting the current user. I implemented this by creating an IUserContext abstraction and injecting it at the scoped level:
public sealed class AspNetUserContext : IUserContext
{
private readonly UserManager<User> userManager;
private readonly IHttpContextAccessor accessor;
private User currentUser;
public AspNetUserContext(IHttpContextAccessor a, UserManager<User> userManager) {
accessor = a;
if (userManager == null)
throw new ArgumentNullException("userManager");
this.userManager = userManager;
}
public string Name => accessor.HttpContext.User?.Identity?.Name;
public int Id => accessor.CurrentUserId();
public User CurrentUser {
get {
if (currentUser == null) {
currentUser = this.UserManager.FindByIdAsync(Id.ToString()).ConfigureAwait(false).GetAwaiter().GetResult();
}
return currentUser;
}
}
}
I am struggling trying to find out how to correctly initialize the CurrentUser property.
Since there is no longer any way to get a user from the UserManager class sychronously, I am not comfortable running an async method from within a property getter when initializing the CurrentUser, nor from the constructor (there are no long any synchronous methods on the UserManager class with ASP.NET Core).
I feel like the correct way to do this would be to run an initialization method on the injected instance somehow once per request since it is scoped (perhaps with an action filter/middleware/Controller base class (or perhaps in the dependency injection AddScoped method itself as a factory method?)
This seems like a pretty common problem and I'm wondering how others have resolved this.
In this case you will need to forgo the property and have an asynchronous method.
This would also mean having an asynchronous lazy initialization for the User using
/// <summary>
/// Provides support for asynchronous lazy initialization.
/// </summary>
/// <typeparam name="T"></typeparam>
public class LazyAsync<T> : Lazy<Task<T>> {
/// <summary>
/// Initializes a new instance of the LazyAsync`1 class. When lazy initialization
/// occurs, the specified initialization function is used.
/// </summary>
/// <param name="valueFactory">The delegate that is invoked to produce the lazily initialized Task when it is needed.</param>
public LazyAsync(Func<Task<T>> valueFactory) :
base(() => Task.Run(valueFactory)) { }
}
This now makes it possible to refactor the context to use lazy initialization,
public sealed class AspNetUserContext : IUserContext {
private readonly UserManager<User> userManager;
private readonly IHttpContextAccessor accessor;
private readonly LazyAsync<User> currentUser;
public AspNetUserContext(IHttpContextAccessor accessor, UserManager<User> userManager) {
this.accessor = accessor;
if (userManager == null)
throw new ArgumentNullException(nameof(userManager));
this.userManager = userManager;
currentUser = new LazyAsync<User>(() => this.userManager.FindByIdAsync(Id.ToString()));
}
public string Name => accessor.HttpContext.User?.Identity?.Name;
public int Id => accessor.CurrentUserId();
public Task<User> GetCurrentUser() {
return currentUser.Value;
}
}
And used where needed
User user = await context.GetCurrentUser();
Now a property could have still been used like
public Task<User> CurrentUser => currentUser.Value;
as the getter is a method, but that is not a very intuitive design in my personal opinion.
User user = await context.CurrentUser;
and can have undesirable results if accessed too early.
I only mention it because of the design of the original context shown in the example.
You can try following,
1) Register IUserContext and UserContext as Scoped
2) Retrieve IUSerContext or UserContext via httpContext,
IUserContext userContext = (IUserContext)httpContext.RequestServices.GetService(typeof(IUserContext));
My personal approach is a little bit different from nkosi's answer.
Your final requirement is to access one or more User property, right?
So, you can take advantange by using delegates mixed with async:
public interface IUserAccessor
{
Task<TValue> GetValueAsync<TValue>(Func<User, TValue> func);
}
public class UserAccessor
{
public UserAccessor(MyDbContext db)
{
this.db = db;
}
private readonly MyDbContext db;
public async Task<TValue> GetValueAsync<TValue>(Func<User, TValue> func)
{
return func(await this.db.FindUsersBySomeConditions().FirstOrDefaultAsync());
}
}
If your service is scoped, you can also use a persistence and avoid a second call to DB:
private User user;
public async Task<TValue> GetValueAsync<TValue>(Func<User, TValue> func)
{
if (this.user == null)
{
this.user = await this.db.FindUsersBySomeConditions().FirstOrDefaultAsync();
}
return func(this.user);
}
You can use like this:
#inject IUserService userService
// ...
<div>
<div>#await userService.GetValueAsync(u => u.Name)</div>
<div>#await userService.GetValueAsync(u => u.Role)</div>
<div>#await userService.GetValueAsync(u => u.BirthDate)</div>
<div>#await userService.GetValueAsync(u => u.ComplexProperty.SubProperty)</div>
</div>
I am trying to add one more parameter to my constructors in my Asp.Net Core MVC application, but facing some difficulties to do so. Here is what my implementation looks like.
Login action:
[HttpPost, AllowAnonymous, ValidateAntiForgeryToken]
public IActionResult Login(LoginViewModel loginModel, string returnUrl = null)
{
returnUrl = string.IsNullOrWhiteSpace(returnUrl) ? ApiConstants.Dashboard : returnUrl;
ViewData["ReturnUrl"] = returnUrl;
if (!ModelState.IsValid) return View(loginModel);
var token = Service.Login(loginModel);
if (string.IsNullOrWhiteSpace(token)) return View(loginModel);
TempData["token"] = token;
AddCookie(token);
return RedirectToAction("Index", "Dashboard");
}
private void AddCookie(string token)
{
HttpContext.Response.Cookies.Append("token", token,new CookieOptions()
{
Expires = DateTimeOffset.Now.AddDays(-1)
});
}
Controller:
private readonly INozzleService _nozzleService;
public NozzleController(INozzleService nozzleService)
{
var token = HttpContext.Request.Cookies["token"];
_nozzleService = nozzleService;
}
Nozzle Service:
private static INozzleAdapter Adapter { get; set; }
public NozzleService(INozzleAdapter adapter)
{
Adapter = adapter;
}
Nozzle Adapter:
private readonly string _token;
public NozzleAdapter(string token)
{
_token = token;
}
Once I get the token in the adapter, I will be adding the token to the HttpClient header.
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _token);
ConfigureServices in Startup:
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<ITempDataProvider, CookieTempDataProvider>();
services.AddDistributedMemoryCache();
services.AddSession();
services.AddTransient<IAccountService, AccountService>();
services.AddTransient<IAccountAdapter, AccountAdapter>();
services.AddTransient<INozzleService, NozzleService>();
services.AddTransient<INozzleAdapter, NozzleAdapter>();
services.AddMvc();
}
Can you please let me know what could be the best way to achieve this in Asp.Net core 2.0 MVC application? I have read a post saying that using multiple constructors is not a good idea in Asp.Net Core MVC application, so I don't want to use multiple constructors.
At the same time, I want to make sure all of my classes are unit testable with DI. What should be the best approach here?
Please let me know if anyone needs more information.
Update:
As per Shyju's solution, I was able to implement the cookie, however, I am still in a need to pass two parameters to one of my controllers.
private readonly IAccountService _service;
private readonly ITokenProvider _tokenProvider;
public AccountController(IAccountService service, ITokenProvider tokenProvider)
{
_service = service;
_tokenProvider = tokenProvider;
}
So that I can, use the method AddToken as below.
_tokenProvider.AddToken(token);
You may consider abstracting out the logic to get the token to a separate class and inject that as needed.
public interface ITokenProvider
{
/// <summary>
/// Gets the token
/// </summary>
/// <returns></returns>
string GetToken();
}
Now create an implementation of this, which will be reading the token from the cookie. Here is a simple implementation, which reads the token from the cookies collection
public class CookieTokenProvider : ITokenProvider
{
private readonly IHttpContextAccessor httpContextAccessor;
public CookieTokenProvider(IHttpContextAccessor httpContextAccessor)
{
this.httpContextAccessor = httpContextAccessor;
}
public string GetToken()
{
if (httpContextAccessor.HttpContext.Request.Cookies
.TryGetValue("token", out string tokenValue))
{
return tokenValue;
}
return null;
}
}
Now, you can inject the ITokenProvider implementation to anyplace you want and call the GetToken method to get the token value. For example, you may inject this to the NozzleAdapter class constructor.
private readonly ITokenProvider tokenProvider;
public NozzleAdapter(ITokenProvider tokenProvider)
{
tokenProvider=tokenProvider;
}
public string SomeOtherMethod()
{
var token = this.tokenProvider.GetToken();
//Do null check and use it
}
Make sure you register this in the ConfigureServices method in Startup class
services.AddTransient<ITokenProvider, CookieTokenProvider>();
Regarding your comment about getting the token and persisting it, it is up to you where you want to do it. You can do that in the CookieTokenProvider implementation. Read the value and store it somewhere ( a local db, in memory cache etc) and get it from there if exists (the next time)
Now, for your unit tests you can create a MockTokenProvider which does not use HttpContext, but simply return a mock value for your testing,
public class MockTokenProvider : ITokenProvider
{
public string GetToken() => "FakeToken";
}
As with most of my recent posts, I will start by saying this is all new to me, and I am on the edge of my understanding. I am working on an existing application and have come across the above error.
The application is an MVC application that has 3 layers, and is being run through Swagger:
Rest
Services
DAL
In the service layer DependencyConfig, I do the following in the Register Method, the context is registered:
container.RegisterWebApiRequest<DbContext>(() => new LocalContext("name=DefaultConnection"));
So far this all seems to be working fine, as parts of the application are reading and writing from the database - although there is a lot in this application and I do not understand all of it.
Next, on the Rest layer, I have the following:
public class AccountController : ApiController
{
private readonly WindowsAuthenticationProvider _windowsAuthentication;
private readonly AuthorizationManager _authorizationManager;
private readonly IApplicationUserService _userService;
private readonly IProfileService _profileService;
private readonly IAccountLogsService _accountLogsService;
/// <summary>
/// Constructor
/// </summary>
/// <param name="authorizationManager">Authorization Manager</param>
/// <param name="windowsAuthentication">Enables windows reauthentication without a session</param>
public AccountController(AuthorizationManager authorizationManager, WindowsAuthenticationProvider windowsAuthentication,
IApplicationUserService userService, IProfileService profileService, IAccountLogsService accountLogsService)
{
_windowsAuthentication = windowsAuthentication;
_authorizationManager = authorizationManager;
_userService = userService;
_profileService = profileService;
_accountLogsService = accountLogsService;
}
/// <summary>
/// Get logged in user details
/// </summary>
/// <remarks>Retrieves details of the currently logged in user</remarks>
[Authorize]
[HttpGet]
[Route("")]
[ResponseType(typeof(AccountDetailsModel))]
public IHttpActionResult Details()
{
var userId = User.Identity.GetUserId<int>();
var model = //...
if (model.HasAccount)
{
var user = _authorizationManager.FindUser(User.Identity);
}
return Ok(model);
}
}
and also from the services layer:
public class AuthorizationManager
{
private readonly IApplicationUserService _userService;
public ApplicationUser FindUser(IIdentity identity)
{
var userId = identity.GetUserId<int>();
if (userId != 0)
{
return _userService.FindById(userId);
}
//...
}
}
and finally also on the services layer:
public class ApplicationUserService : UnitOfWork<LocalContext>, IApplicationUserService
{
private readonly UserManager<ApplicationUser, int> _userManager;
private readonly RoleManager<ApplicationRole, int> _roleManager;
/// <summary>
/// Finds a user by id
/// </summary>
/// <param name="id">User id</param>
/// <returns>ApplicationUser</returns>
public ApplicationUser FindById(int id)
{
return _userManager.FindById(id);
}
}
Where ApplicationUser is a model on the DAL layer.
I hope this all makes sense so far!
The problem is that when the code gets to this line:
return _userManager.FindById(id);
it throws an InvalidOperationException: The operation cannot be completed because the DbContext has been disposed.
Which seems to me to be saying it has lost the context eg database connection.
It seems to have disposed of the DbContext from the register method - but I have no idea why?
If I manually create a context like this:
Context.Users.Find(id);
I am able to retrieve the required entry, but the problem re occurs further down the line - and I do not think the application was intended to work like this.
So my question is, why is it disposing of my context and how can I stop it doing so?
Stepping through the code does not help and I am out of ideas.
The problem was to do with this line:
container.Register<IUserStore<ApplicationUser, int>, UserStore <ApplicationUser, ApplicationRole, int, ApplicationUserLogin, ApplicationUserRole, ApplicationUserClaim>>(Lifestyle.Scoped);
Because of lifestlye.scoped, the context was being disposed of at this point.
I needed to add this to the container:
container.Options.DefaultScopedLifestyle = new WebApiRequestLifestyle();
from these instructions: http://simpleinjector.readthedocs.io/en/latest/lifetimes.html#perwebrequest
Which only disposed of the object at the end of the web request
Ok, i tried. but i can't wrap my head around this.
I have a Controller
public sealed class CourseController : ExtController
{
[HttpPost, PersistState, InRole("")] //TODO [SECURITY] [FIX] UPDATE SECURITY ROLES ]
public ActionResult Create(string[] flags, string name, string code, string description)
{
try
{
var Course = Svc.ProcessOperation("CreateCourse", new
{
Flags = flags.Merge(",")
});
Svc.ProcessOperation("CreateCourseTranslation", new
{
CourseId = Course.EntityID,
LanguageId = JAs.Int32(Svc.Localization.Language.EntityID),
Name = name,
Description = description,
Code = code
});
TempData.PersistStatus("Success");
return View();
}
catch (Exception ex)
{
ModelState.AddModelError("API", ex);
TempData.PersistStatus("Failed");
}
return RedirectToAction("Create");
}
}
Svc is a public property of type Service inside the ExtController abstract class which in turn extends the Controller Class
/// <summary>
/// Represents the exteded controller.
/// </summary>
public abstract class ExtController : Controller
{
#region Properties
/// <summary>
/// Gets the service associated with the controller.
/// </summary>
public Service Svc
{
get
{
return HttpContext.Svc();
}
}
#endregion
}
and here is the Unit Test Code using NUnit
[Test]
public void Create_HttpPost_Action_Returns_Create_View()
{
// Arrange
var customersController = new CourseController();
// Act
var result = customersController.Create(new[] { "None" }, "courseName", "Code", "description") as ViewResult;
// Assert
Assert.IsNotNull(result, "Should have returned a ViewResult");
result.AssertViewRendered().ForView("Create");
}
The Problem is when the Create method is called it needs to use Svc to process the operation, so i guess i have to Mock that! but i can't figure out how.
should i mock the controller! but i can't because its a sealed class! or the ExtController! I am lost and need guidance.
[FWIW]
This project is based on Xenta MVC Framework (Open Source) which has this Architecture Overview
You should not mock the controller because that is what you are trying to test. Like you said you must mock the Svc property. One possible solution is to make the property overridable in your abstract ExtController and then override it in CourseController. You can now set your Svc property to a mock in the unit test.