Moq Parent does not have a default constructor. The default constructor must be explicitly defined - asp.net

I am using entity framework db first approach and generated edmx file.
I am trying to implement enter code here Unit testing with MOQ framework.
Here is my code
[TestMethod]
public void DeleteApplication()
{
var mockContext = new Mock<ARMSContext>();
var data = GetMemoryApplications();
var mockSet = new Mock<DbSet<Application>>();
mockSet.As<IQueryable<Application>>().Setup(m => m.Provider).Returns(data.Provider);
mockSet.As<IQueryable<Application>>().Setup(m => m.Expression).Returns(data.Expression);
mockSet.As<IQueryable<Application>>().Setup(m => m.ElementType).Returns(data.ElementType);
mockSet.As<IQueryable<Application>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());
var test=appcontroller.Delete(1, 1);
}
I am getting the Parent does not have.. exception while calling mockSet.Object.
could you pls to resolve this exception?

From what you have given, I can't see how you are using Mock<DbSet<Application>>, nor Mock<ARMSContext>.
Here is the sample given from MSDNs "Testing with a mocking framework (EF6 onwards)", which hooks the DbSet up to the Context:
[TestMethod]
public void GetAllBlogs_orders_by_name()
{
var data = new List<Blog>
{
new Blog { Name = "BBB" },
new Blog { Name = "ZZZ" },
new Blog { Name = "AAA" },
}.AsQueryable();
var mockSet = new Mock<DbSet<Blog>>();
mockSet.As<IQueryable<Blog>>().Setup(m => m.Provider).Returns(data.Provider);
mockSet.As<IQueryable<Blog>>().Setup(m => m.Expression).Returns(data.Expression);
mockSet.As<IQueryable<Blog>>().Setup(m => m.ElementType).Returns(data.ElementType);
mockSet.As<IQueryable<Blog>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());
var mockContext = new Mock<BloggingContext>();
mockContext.Setup(c => c.Blogs).Returns(mockSet.Object);
var service = new BlogService(mockContext.Object);
var blogs = service.GetAllBlogs();
Assert.AreEqual(3, blogs.Count);
Assert.AreEqual("AAA", blogs[0].Name);
Assert.AreEqual("BBB", blogs[1].Name);
Assert.AreEqual("ZZZ", blogs[2].Name);
}
}

Related

Kept running into 401 unauthorize response using .NET Core v3.1

I upgraded the source code to .NET Core v3.1 & I'm having trouble figuring how to debug the backend issue due to lot of dependency injections, abstractions & overriding classes/methods all over. The employee who wrote this have overcomplicate things & he had left the company so we got stuck with the confusing source code mess here that take a lot of our time & energy, to make sense of the it. :-/
The error I'm having is a 401 unauthorize response. I discovered the debugger doesnt respond in StartUp class when consuming the webservice, it only respond when you start up the Web App. So, it took us a while & finally found a hitting debugger breakpoint on a MVC controller page to point us in the right direction. There it is saying the Identity is not authenticated so that explain the unauthorize error.
We're not familiar with this one authentication technology, Odachi. We believe there are 2 seperate authentication architecture, which is ehe WebApp's webpages login authorization for the customer's web browser & Odachi deal with the WebApp's webservice login authorization for the 3rd party software making the webservice call.
Source code below is the webservice MVC controller w/ Authorization filter. Then further down will be the Startup w/ base Startup abstraction.
[ Webservice call ]
namespace ABC.Payments.AspNet.MVC
{
public class AuthorizeWithNoChallengeFilterAttribute : IAuthorizationFilter
{
public void OnAuthorization(AuthorizationFilterContext context)
{
if (context.HttpContext.User?.Identity.IsAuthenticated != true)
context.Result = new UnauthorizedResult();
}
}
}
namespace ABC.Payments.MerchantWeb.Api
{
[TypeFilter(typeof(AuthorizeWithNoChallengeFilterAttribute))]
public class MerchantsV1Controller : Controller
{
[Route("api/v1/merchants/{merchantAccountId}/customers/payments/paymentmethods"), HttpPost]
public async Task<ObjectResult> Payment([FromBody] ItemInfoViewModel itemInfo, CancellationToken cancellationToken)
{
var payments = whatever();
return new HttpNotAcceptableObjectResult(payments);
}
}
}
[ Startup & Startup Base ]
namespace ABC.Payments.MerchantWeb
{
// StackOverflow Post on how to find 401 Unathorize error (debug)
// --> https://stackoverflow.com/questions/43574552/authorization-in-asp-net-core-always-401-unauthorized-for-authorize-attribute
public class Startup : StartupBase<MerchantRequestContext, Merchant>
{
private const string _schemeCustomMerchantBasic = "CustomMerchantBasic";
public Startup(IWebHostEnvironment webHostEnvironment)
: base(webHostEnvironment, PortalRoleType.Merchant)
{
}
public void ConfigureServices(IServiceCollection services)
{
base._configureServices(true, services);
services.AddTransient(sp => sp.GetService<MerchantRequestContext>()?.Merchant);
services.AddTransient(sp => sp.GetService<MerchantRequestContext>()?.Customer);
services.AddTransient(sp => sp.GetService<MerchantRequestContext>()?.TenantSettings);
services.AddLocalization(options =>
{
options.ResourcesPath = "Resources";
});
services.AddMvcCore()
.AddViewLocalization(LanguageViewLocationExpanderFormat.SubFolder, setup =>
{
setup.ResourcesPath = "Resources";
})
.AddDataAnnotationsLocalization()
.AddApiExplorer();
services.AddCors(options =>
{
options.AddPolicy("Internal", p => p.WithOrigins(base._configuration["Cors:InternalSource"]).WithMethods("POST").WithHeaders("accept", "request", "authorization", "content-type", "internal"));
});
services.AddAuthentication()
// https://github.com/Kukkimonsuta/Odachi/blob/master/src/Odachi.AspNetCore.Authentication.Basic/Events/BasicSignInContext.cs (Basic Sign Context)
// https://github.com/Kukkimonsuta/Odachi/blob/master/samples/BasicAuthenticationSample/Startup.cs
.AddBasic(_schemeCustomMerchantBasic, options =>
{
// ////////Notice: AutomaticChallenge is depreciated, google search said to use DefaultChallengeScheme w/ given cookie-authentication-scheme but that still doesnt explain how to disable it
// //////// https://stackoverflow.com/questions/45878166/asp-net-core-2-0-disable-automatic-challenge
// //////// https://github.com/dotnet/aspnetcore/issues/2007
//## options.AutomaticChallenge = false;
options.Realm = "AutoPayment API v1";
options.Events = new BasicEvents()
{
OnSignIn = async context =>
{
var claims = new List<Claim>();
if (context.Username == "ndi3DanDba993nvbaqbn3d93" && context.Password == "aVd3Ed51dfDE5acCCni9l1IxPq9")
claims.Add(new Claim(ClaimTypes.Role, "InternalAPIUser"));
else
{
string merchantAccountId = context.Request.Path.Value.Split('/').Skip(4).FirstOrDefault();
var merchantRepository = context.HttpContext.RequestServices.GetRequiredService<IMerchantRepository>();
if (merchantAccountId == null || merchantAccountId.Length != 14 || merchantAccountId.Split('-').Length != 3)
throw new Exception($"Invalid merchant account Id ({merchantAccountId ?? string.Empty}).");
var merchant = await merchantRepository.GetMerchantAsync(merchantAccountId, context.HttpContext.RequestAborted);
if (merchant == null || !merchant.IsActive || (merchant.GatePayApiKey != context.Username || merchant.GatePayApiSecret != context.Password))
{
context.Fail("Invalid merchant"); //## context.HandleResponse();
return;
}
}
var principal = new ClaimsPrincipal(new ClaimsIdentity(claims, context.Scheme.Name)); //## options.AuthenticationScheme));
context.Principal = principal;
//## context.Ticket = new AuthenticationTicket(principal, new AuthenticationProperties(), options.AuthenticationScheme);
context.Success(); //## context.HandleResponse();
//return Task.CompletedTask;
}
};
});
}
public void Configure(IApplicationBuilder applicationBuilder, ILoggerFactory loggerFactory, IServiceProvider serviceProvider)
{
base._configure(true, applicationBuilder, loggerFactory, serviceProvider);
applicationBuilder.UseCors("Internal");
applicationBuilder.UseWhen(context => !context.Request.Path.StartsWithSegments(new PathString("/api/v1")), b => b.UseAuthentication());
}
}
}
namespace ABC.Payments
{
public class StartupBase<TRequestContext, TUserContext>
where TRequestContext : RequestContext<TUserContext>
{
public StartupBase(IWebHostEnvironment webHostEnvironment, PortalRoleType portalRoleType)
{
_portalRoleType = portalRoleType;
_webHostEnvironment = webHostEnvironment;
var builder = new ConfigurationBuilder();
ConfigurationLoader.Load(builder, webHostEnvironment);
_configuration = builder.Build();
if (webHostEnvironment.EnvironmentName.Equals("Production", StringComparison.OrdinalIgnoreCase) == true && _configuration["ConfirmProduction"]?.Equals("Yes", StringComparison.OrdinalIgnoreCase) != true)
throw new Exception("Azure defaults to \"Production\" for the environment, so you need to create an AppSetting of \"ConfirmProduction\" to \"Yes\" to ensure that is the intent.");
}
private readonly IWebHostEnvironment _webHostEnvironment;
public readonly IConfiguration _configuration;
private readonly PortalRoleType _portalRoleType;
public void _configureServices(bool isWebBrowserFrontendGui, IServiceCollection services)
{
if (isWebBrowserFrontendGui)
{
services.AddDistributedRedisCache(options =>
{
options.Configuration = _configuration["Storage:Redis:Configuration"];
});
services.AddSingleton<RedisCache>();
services.AddSingleton<MemoryDistributedCache>();
services.AddSingleton<IDistributedCache>(
sp => new ResilientDistributedCache(sp.GetRequiredService<RedisCache>(), sp.GetRequiredService<MemoryDistributedCache>())
);
var azureBlobConnectionTring = _configuration["Storage:AzureBlob:ConnectionString"];
if (azureBlobConnectionTring != null)
{
var storageAccount = CloudStorageAccount.Parse(azureBlobConnectionTring);
var client = storageAccount.CreateCloudBlobClient();
var azureBlobContainer = client.GetContainerReference("dataprotection-key-container");
services.AddDataProtection().PersistKeysToAzureBlobStorage(azureBlobContainer, "keys.xml");
}
services.AddSession(options =>
{
//options.IdleTimeout = TimeSpan.FromMinutes(5);
});
services.AddDefaultIdentity<ApplicationUser>()
.AddRoles<IdentityRole<Guid>>()
.AddEntityFrameworkStores<ApplicationContext>() // FYI - AddEntityFrameworkStores() deal with role that derives from IdentityRole, as per documentation.
.AddDefaultTokenProviders();
services.ConfigureApplicationCookie(options => {
options.LoginPath = new PathString("/Home/Index");
options.SlidingExpiration = true;
options.ExpireTimeSpan = TimeSpan.FromMinutes(_configuration.GetValue<int?>("Authentication:SlidingExpirationTime").Value);
options.AccessDeniedPath = new PathString("/Home/AccessDenied");
});
services.Configure<IdentityOptions>(options => {
options.Password.RequireUppercase = false;
options.Password.RequireLowercase = false;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequireDigit = false;
options.Password.RequiredLength = 7;
});
services.AddControllersWithViews();
services.AddRazorPages();
// AddMvc() vs AddMvcCore() explaination found at --> https://offering.solutions/blog/articles/2017/02/07/the-difference-between-addmvc-and-addmvccore/
// --> https://stackoverflow.com/questions/42365275/how-to-implement-a-pure-asp-net-core-web-api-by-using-addmvccore/42365276#42365276
services.AddMvc().AddRazorRuntimeCompilation();
services.Configure<MvcRazorRuntimeCompilationOptions>();
services.Configure<AuthorizationOptions>(options =>
{
options.DefaultPolicy = AuthorizationPolicy.Combine(options.DefaultPolicy,
new AuthorizationPolicy(new IAuthorizationRequirement[] {
new RolesAuthorizationRequirement(new string[] { _portalRoleType.ToString(), PortalRoleType.Internal.ToString() }),
new ImpersonationNotExpiredAuthorizationRequirement(_portalRoleType, _configuration.GetValue<TimeSpan?>("Authentication:ImpersonationTimeLimit").Value)
}, new string[0]));
});
services.AddMvcCore(options =>
{
var requestContextAttribute = new LoadRequestContextAttribute(typeof(TRequestContext));
options.Filters.Add(requestContextAttribute);
options.ModelBinderProviders[options.ModelBinderProviders.IndexOf(
options.ModelBinderProviders.OfType<ComplexTypeModelBinderProvider>().First()
)] = new TryServicesModelBinderProvider(services.BuildServiceProvider());
options.ModelBinderProviders.Insert(0, new EnumModelBinderProvider(services.BuildServiceProvider()));
})
.AddDataAnnotationsLocalization()
.AddNewtonsoftJson(settings =>
{
settings.SerializerSettings.ContractResolver = new DefaultContractResolver();
});
services.Configure<ForwardedHeadersOptions>(options => options.RequireHeaderSymmetry = false);
}
//services.AddPayments<TRequestContext, TUserContext>(_configuration, string.Empty);
}
public void _configure(bool isWebBrowserFrontendGui, IApplicationBuilder applicationBuilder, ILoggerFactory loggerFactory, IServiceProvider serviceProvider)
{
if (isWebBrowserFrontendGui)
{
serviceProvider.GetRequiredService<ITelemeter<StartupBase>>().TrackMetric("Startup Time", (DateTime.UtcNow - DateTime.UtcNow).TotalSeconds);
// Exception Page Handling.
if (!_webHostEnvironment.IsProduction())
{
applicationBuilder.UseDeveloperExceptionPage();
//applicationBuilder.UseDatabaseErrorPage();
}
else
applicationBuilder.UseExceptionHandler("/Home/ErrorPage.html");
applicationBuilder.UseStaticFiles(); // Note, we are not authenticating for static files if this is before them
//applicationBuilder.UseStatusCodePages();
// Session.
applicationBuilder.UseSession();
applicationBuilder.UseAuthentication();
// Routing.
applicationBuilder.UseRouting();
applicationBuilder.UseAuthorization(); // Exception error said to put this between UseRouting() & UseEnpoint().
applicationBuilder.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(name: "default", pattern: "{controller=Home}/{action=Index}/{id?}");
endpoints.MapRazorPages();
});
// Config Localization.
var options = serviceProvider.GetService<IOptions<RequestLocalizationOptions>>();
if (options != null)
applicationBuilder.UseRequestLocalization(options.Value);
applicationBuilder.UseForwardedHeaders(new ForwardedHeadersOptions { ForwardedHeaders = ForwardedHeaders.All });
// Ensure Https.
var portals = applicationBuilder.ApplicationServices.GetRequiredService<Portals>();
applicationBuilder.Use(next => async httpContext =>
{
if (httpContext.Request.Host.Value.Contains("localhost"))
{
await next(httpContext);
}
else
{
string host = portals.GetHostForRedirect(httpContext.Request.Host.Value);
if (!host.Equals((httpContext.Request.IsHttps ? "https://" : "http://") + httpContext.Request.Host, StringComparison.OrdinalIgnoreCase))
httpContext.Response.Redirect($"{host}{httpContext.Request.Path}{httpContext.Request.QueryString}");
else
await next(httpContext);
}
});
}
//applicationBuilder.UsePayments<TRequestContext, TUserContext>();
}
}
}

ASP.NET Core Date/Time Localization

I'm implementing localization in my ASP.NET Core 3.1 MVC app to support multiple languages in the UI, but I'm running into issues due to the different calendars each culture uses. How do I set Gregorian as the calendar type for all cultures?
Here is my ConfigureServices method in startup.cs:
public void ConfigureServices(IServiceCollection services)
{
var cultures = new[]
{
new CultureInfo("ar"),
new CultureInfo("en")
};
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")
)
);
services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
services.AddControllersWithViews();
services.AddRazorPages()
.AddExpressLocalization<ExpressLocalizationResource, ViewLocalizationResource>(
ops =>
{
ops.ResourcesPath = "LocalizationResources";
ops.RequestLocalizationOptions = o =>
{
o.SupportedCultures = cultures;
o.SupportedUICultures = cultures;
o.DefaultRequestCulture = new RequestCulture("en");
};
}
);
}
Turns out you can change a CultureInfo's calendar through its DateTimeForm property. Something like this:
var supportedCultures = new List<CultureInfo> { };
var ar = new CultureInfo("ar");
ar.DateTimeFormat.ShortDatePattern = "dd/MM/yyyy";
ar.DateTimeFormat.DateSeparator = "/";
ar.DateTimeFormat.Calendar = new GregorianCalendar();
supportedCultures.Add(ar);
You can change the calendar for a culture.
So for your example the following code would change the "ar" culture to use a gregorian calendar.
CultureInfo ar = CultureInfo.CreateSpecificCulture("ar");
ar.DateTimeFormat.Calendar = new GregorianCalendar();
var cultures = new[]
{
ar,
new CultureInfo("en")
};
Documentation for calendars and cultureinfo
https://learn.microsoft.com/en-us/dotnet/standard/datetime/working-with-calendars

Moq how to test BusinessLayer

I have problem with the Unit Test my Module from Business Layer.
I'm using Moq.
my Module from DL :
namespace EF.BusinessLayer.Modules
{
public class UserModule : IUserModule
{
public User AddUser(User user)
{
using (IUnitOfWork uow = IoC.Resolve<IUnitOfWork>())
{
uow.Add(user);
uow.SaveChanges();
return uow.Queryable<User>().Where(x => x.Username == user.Username).FirstOrDefault();
}
}
}
}
I'm trying to write a test, with which I can test if my entity was added properly.
[TestMethod]
public void AddUserTestMethod()
{
User user = new User()
{
FirstName = "Criss",
LastName = "Johnson",
Username = "CJ",
Email = "email#cj.com"
};
var mockContext = new Mock<IUserModule>();
mockContext.Setup<User>(x => x.AddUser(user)).Callback<User>((c) => User = c);
var result = mockContext.Object.AddUser(user);
}
but results = null.
What I'm doing wrong ?
Lets understand what is your system under test (SUT). From your description, I think it's UserModule.
Note that UserModule depends on IUnitOfWork, which you have very rightly dependency injected using an IoC container.
One of the key things of unit testing is to test the SUT in isolation. Which means for testing the business logic under UserModule, you should be mocking the IUnitOfWork. That's the problem I see with your test. It mocks the UserModule itself.
I suggest you to register the mocked IUnitOfWork in your IoC container before calling the "AddUser" method of UserModule.
[TestMethod]
public void AddUserTestMethod()
{
/*given*/
User expectedUser = new User()
{
FirstName = "Criss",
LastName = "Johnson",
Username = "CJ",
Email = "email#cj.com"
};
var users = new List<User>();
users.Add(expectedUser);
//mock IUnitWork
var mockUnitOfWork= new Mock<IUnitOfWork>();
mockUnitOfWork.Setup(x => x.Add(user));
mockUnitOfWork.Setup(x => x.SaveChanges());
//This may not work as is, could need modification
mockUnitOfWork.Setup(x => x.Queryable<User>()).Returns(users);
//Register mocked unit of work in IoC container
IoC.Register<IUnitOfWork>(mockUnitOfWork.Object);
//Instantiate SUT
var userModule = new UserModule();
/*when*/
var result = userModule.AddUser(user);
/*then*/
//Assert here
}

Moq Setup returning null

I set up my mock object using Moq like this:
var accountRepositoryMock = new Mock<IGenericRepository<Account>>();
accountRepositoryMock.Setup(r => r.SingleOrDefault(a => a.AccountId == It.IsAny<long>())).Returns(new Account { AccountId = 99999999, Valid = true });
var unitOfWorkMock = new Mock<IUnitOfWork>();
unitOfWorkMock.SetupGet(unitofwork => unitofwork.AccountRepository).Returns(accountRepositoryMock.Object);
Then I pass it to my service like this:
IQuickPayService quickPayService = new QuickPayService(unitOfWorkMock.Object);
Account account = quickPayService.ValidateAccount(accountId);
When I do this in my client code, I am getting account as null
public class QuickPayService : IQuickPayService
{
public QuickPayService(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
public AccountStatus ValidateAccount(long accountId)
{
var account;
using (_unitOfWork)
{
account = _unitOfWork.AccountRepository.SingleOrDefault(acc => acc.AccountId == 99999999);
}
return account;
}
}
Any ideas on what I am doing wrong?
Assuming that your interface is declared similarly to the following:
public interface IGenericRepository<T>
{
T SingleOrDefault(Func<T, bool> predicate);
}
Specifying a delegate in Setup won't work. See Issue 300: Mocking Method with Delegate as Parameter. I get a NotSupportedException when I tried it.
Instead, try this:
accountRepositoryMock.Setup(r => r.SingleOrDefault(It.IsAny<Func<Account, bool>>()))
.Returns(new Account { AccountId = 99999999, Valid = true });
There's a more detailed example at Moq framework Func<T,T>

Moq Event Aggregator Is it possible

Wondering if its possible to Moq the Prism EventAggregator
Let's take the EventAggregator Quickstart they have
[TestMethod]
public void PresenterPublishesFundAddedOnViewAddClick()
{
var view = new MockAddFundView();
var EventAggregator = new MockEventAggregator();
var mockFundAddedEvent = new MockFundAddedEvent();
EventAggregator.AddMapping<FundAddedEvent>(mockFundAddedEvent);
var presenter = new AddFundPresenter(EventAggregator);
presenter.View = view;
view.Customer = "99";
view.Fund = "TestFund";
view.PublishAddClick();
Assert.IsTrue(mockFundAddedEvent.PublishCalled);
Assert.AreEqual("99", mockFundAddedEvent.PublishArgumentPayload.CustomerId);
}
I have tried to convert the above using moq but I get problems
they have MockEventAggregator.How can I do that using Moq?
public class MockEventAggregator : IEventAggregator
{
Dictionary<Type, object> events = new Dictionary<Type, object>();
public TEventType GetEvent<TEventType>() where TEventType : EventBase
{
return (TEventType)events[typeof(TEventType)];
}
public void AddMapping<TEventType>(TEventType mockEvent)
{
events.Add(typeof(TEventType), mockEvent);
}
}
Has anybody used MOQ and the EventAggregator are there any examples out there?
Thanks a lot
EDIT
Following GrameF Answer I have added my code that still does not work.Can you help
[TestMethod]
public void PresenterPublishesFundAddedOnViewAddClick2()
{
//Arrange
var view = new Mock<IAddFundView>();
var fakeEventAggregator = new Mock<IEventAggregator>();
var fakeMyEvent = new Mock<FundAddedEvent>();
fakeEventAggregator.Setup(x => x.GetEvent<FundAddedEvent>()).Returns(fakeMyEvent.Object);
var presenter = new AddFundPresenter(fakeEventAggregator.Object) {View = view.Object};
fakeMyEvent.Verify(x => x.Publish(It.IsAny<FundOrder>())); **//CRASHES** HERE
//view.PublishAddClick();
//view.Customer = "99";
//view.Fund = "TestFund";
//view.PublishAddClick();
////Assert
//Assert.IsTrue(mockFundAddedEvent.PublishCalled);
//Assert.AreEqual("99", mockFundAddedEvent.PublishArgumentPayload.CustomerId);
//Assert.AreEqual("TestFund", mockFundAddedEvent.PublishArgumentPayload.TickerSymbol);
}
Yes, it's possible, you just need to set it up to return a mock event on which you can verify that Publish or Subscribe was called:
var fakeEventAggregator = new Mock<IEventAggregator>();
var fakeMyEvent = new Mock<MyEvent>();
fakeEventAggregator.
Setup(x => x.GetEvent<MyEvent>()).
Returns(fakeMyEvent.Object);
var test = new Foo(fakeEventAggregator.Object);
test.PublishAnEvent();
fakeMyEvent.Verify(x => x.Publish(It.IsAny<MyEventArgs>()));

Resources