Add ViewFolder in config for Spark in ASP.NET MVC Unit Test project - asp.net-mvc-3

I'm sending an email from my ASP.NET MVC app using the Spark View Engine based on this example by Andrew Kharlamov.
I've setup a unit test, CanSendEmail, but I need to specify the viewfolder in the config.
I found the documentation here and the examples give this:
<spark>
<views>
<add name="{any-unique-name}"
folderType="FileSystem|EmbeddedResource|VirtualPathProvider|Custom"
type="{name, assembly of IViewFolder type}"
constuctor-param-names="values"
subfolder="{optional subfolder to target}"/>
</views>
</spark>
My question is this. Which folderType do I use and do I need any other parameters. My test product is call myProject.Tests and my web project containing the views is called myProject.Web with a Views folder in it.
Do I use FileSystem, VirtualPathProvider ... ?
Edit [14/11/2011]:
Okay I've got this in my app.config in myProject.Tests:
<views>
<add name="web-view-folder"
folderType="VirtualPathProvider"
virtualBaseDir="~/Views"/>
</views>
I still get "View source file not found." when I run my test. I want the test to use the Views in myproject.Web.

My Solution
Based on the blog posts here and here, and with help from #RobertTheGrey and looking at the tests in the Spark source code, I ended up using ViewFolderType.FileSystem. That worked.
Here's the my code under test:
public string RenderEmailWithCustomViewFolder(string sparkViewName, ViewDataDictionary viewData, Dictionary<string, string> viewFolderParameters)
{
var settings = new SparkSettings()
.SetPageBaseType(typeof (SparkView))
.AddViewFolder(ViewFolderType.FileSystem, viewFolderParameters)
.AddAssembly("MvcContrib");
var engine = new SparkViewEngine(settings);
var sparkViewDescriptor = new SparkViewDescriptor().AddTemplate(sparkViewName);
var view = (SparkView)engine.CreateInstance(sparkViewDescriptor);
try
{
// Merge view data
viewData.Keys.ToList().ForEach(x => view.ViewData[x] = viewData[x]);
// Render the view to a text writer
var writer = new StringWriter();
view.RenderView(writer);
return writer.ToString();
}
finally
{
engine.ReleaseInstance(view);
}
}
And here's my test:
[Test]
public void Can_Render_Order_Confirmation_Email_With_Spark_View_Engine()
{
// Arrange
var order = OrderInstanceFactory.CreateTestOrder();
order.ContactEmail = "test#testicle.com";
var emailService = new EmailService();
var viewData = new ViewDataDictionary();
viewData["Order"] = order;
const string viewFolder = #"../../../../app/myProject.Web/Views";
var viewFolderParameters = new Dictionary<string, string> {{"basePath", viewFolder}};
// Act
var emailBody = emailService.RenderEmailWithCustomViewFolder("Email/OrderConfirmation.spark", viewData, viewFolderParameters);
// Assert
Assert.IsNotNull(emailBody);
Assert.IsTrue(emailBody.Contains("test#testicle.com"));
}
My OrderConfirmation.spark template lives in my web products in the Views/Email/.

If it's an ASP.NET MVC app, then you can use VirtualPathProvider since that hooks into the HttpContext and the rest of the runtime. You would use a FileSystemProvider if you were runnig it from a console app for example, or if you wanted to add a folder from outside your web app, perhaps because the templates were shared by other apps, but I've rarely seen that done.
Hope that helps...

Related

Problem with ConfigurationStoreOptions while upgrading to Duende IdentityServer 6.0

I have some integration tests that use a real database targetting a ConfigurationDbContext. When upgrading to Duende IdentityServer 6.0, the constructor for ConfigurationDbContext breaks (only accepts 1 arg instead of 2) because of the DbContext connection pooling feature that was added.
This code breaks:
public static ConfigurationDbContext GetConfigurationDbContext()
{
var connectionString = Configuration.GetConnectionString("ConfigurationDbContext");
var builder = new DbContextOptionsBuilder<ConfigurationDbContext>();
builder.UseSqlServer(connectionString);
var options = new ConfigurationStoreOptions
{
DefaultSchema = Schema.IdSrv
};
return new ConfigurationDbContext(builder.Options, options);
}
So I changed it to:
return new ConfigurationDbContext(builder.Options);
Now I can build, but my tests fail with this error:
Unable to resolve service for type 'Duende.IdentityServer.EntityFramework.Options.ConfigurationStoreOptions'
How am I supposed to pass the ConfigurationStoreOptions in? Looking at the code in Github, it looks like it relies on dependency injection. (Getting the options from services collection).
OK, I figured out my own problem, but I had to hunt and peck around. It is not listed as a breaking change in the upgrade documentation:
https://docs.duendesoftware.com/identityserver/v6/upgrades/v5.2_to_v6.0/
The solution is to upgrade your project to 6.1
<PackageReference Include="Duende.IdentityServer.EntityFramework.Storage" Version="6.1.5" />
Then you can use this code instead (StoreOptions has been made a public set property)
public static ConfigurationDbContext GetConfigurationDbContext()
{
var connectionString = Configuration.GetConnectionString("MyIdentity");
var builder = new DbContextOptionsBuilder<ConfigurationDbContext>();
builder.UseSqlServer(connectionString);
var options = new ConfigurationStoreOptions
{
DefaultSchema = Schema.IdSrv
};
var dbContext = new ConfigurationDbContext(builder.Options);
dbContext.StoreOptions = options;
return dbContext;
}
This will work for ConfigurationDbContext and PersistedGrantDbContext.

Use MVC's model binding from application code

As a sample of what I'm trying to accomplish, here in MapPost I'm manually parsing the body of the HTTP request.
// Program.cs
using System.Text.Json;
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
Type[] types = new[] { typeof(SampleDto1), typeof(SampleDto2), <other unknown types> };
foreach (var type in types)
{
app.MapPost(type.Name, async (HttpContext httpContext) =>
{
var request = await JsonSerializer.DeserializeAsync(
httpContext.Request.Body,
type,
new JsonSerializerOptions(JsonSerializerDefaults.Web),
httpContext.RequestAborted);
return Results.Ok(request);
});
}
app.Run();
internal record SampleDto1(string Input) { }
internal record SampleDto2(string Input) { }
This works, yay! However, ... ASP.NET Core's MVC has all these sophisticated ModelBinding functionality and I really would like to use that. Because that opens up possibilities for binding to querystring parameters and other sources instead of only the request body.
Basically I want to replace the call to JsonSerializer with a call to framework code.
I've been browsing the ASP.NET Core source code and at first the DefaultModelBindingContext looked promising. However, I soon stumbled on some internal classes which I couldn't access from my code.
Long story short, .. is it at all possible to plug-in to MVC's model binding from application code?
Update: Although it doesn't show from the initial question, the solution should work dynamically with any request type. Not only SampleDto1 and SampleDto2. That's why explicit parameter binding from Minimal API won't do the trick.
You could try the codes :
using Microsoft.AspNetCore.Mvc;
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
builder.Services.AddSingleton<Service>();
app.MapPost("/{id}", ([FromRoute] int id,
[FromQuery(Name = "p")] int page,
[FromBody]SampleDto1 sample1,
[FromBody] SampleDto2 sample2,
[FromServices] Service service,
[FromHeader(Name = "Content-Type")] string contentType)
=> { });
app.Run();
internal record SampleDto1(string Input) { }
internal record SampleDto2(string Input) { }
You could read the official document for more details:
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/minimal-apis?view=aspnetcore-6.0#explicit-parameter-binding

DryIoC register configuration

I am working on a Xamarin project with Prism and DryIoC.
Currently I am setting up some custom environment-specific configuration, however I am struggling with the IoC syntax for this.
I have the following code as part of my App.xaml.cs:
private void SetConfiguration(IContainerRegistry containerRegistry)
{
// Get and deserialize config.json file from Configuration folder.
var embeddedResourceStream = Assembly.GetAssembly(typeof(IConfiguration)).GetManifestResourceStream("MyVismaMobile.Configurations.Configuration.config.json");
if (embeddedResourceStream == null)
return;
using (var streamReader = new StreamReader(embeddedResourceStream))
{
var jsonString = streamReader.ReadToEnd();
var configuration = JsonConvert.DeserializeObject<Configuration.Configuration>(jsonString);
What to do with configuration, in order to DI it?
}
What should I do with the configuration variable to inject it?
I have tried the following:
containerRegistry.RegisterSingleton<IConfiguration, Configuration>(c => configuration);
containerRegistry.Register<IConfiguration, Configuration>(c => configuration));
But the syntax is wrong with dryIoC.
RegisterSingleton and Register are meant for registering types where the container will then create the instances. You have your instance already, so you use
containerRegistry.RegisterInstance<IConfiguration>( configuration );
Instances are always singleton, obviously, so there's only no separate RegisterInstanceSingleton...

ASP.NET BoilerPlate: .Net core template - app service error

I am in the process of moving my app code from the .Net5 MVC/JQuery ASPNETZERO template to the new .Net core 1.1 MVC/JQuery template.
So far I have copied over my first few custom entities. Created a EF migration and applied the changes to my DB.
I then started building the first app service for one of my entities. I am copying over the code from my existing MVC template that is working without any issues.
public async Task<PagedResultDto<ListValuesListDto>> GetListValues(GetListValuesInput input)
{
var query = from lv in _listvaluesRepository2.GetAll()
select new ListValuesListDto
{
Id = lv.Id,
ListName = lv.ListName,
ListText = lv.ListText
};
query = query.WhereIf(!input.Filter.IsNullOrWhiteSpace(), lv => lv.ListName.Contains(input.Filter) || lv.ListText.Contains(input.Filter));
var resultCount = await query.CountAsync();
var results = await query.OrderBy(input.Sorting).PageBy(input).ToListAsync();
return new PagedResultDto<ListValuesListDto>(resultCount, results);
}
The method above is from my MVC template. When I place this into the .Net core solution, I keep getting the following build error.
CS0411 The type arguments for method 'Enumerable.OrderBy(IEnumerable, Func)' cannot be inferred from the usage. Try specifying the type arguments explicitly
The error is pointing to the "OrderBy" on this line of code:
var results = await query.OrderBy(input.Sorting).PageBy(input).ToListAsync();
I am still very much learning the ABP template and EF. I cannot for the life of me figure out why the same code in the .Net core template does not work?
Any help that anyone can provide would be much appreciated!
I just solved my own issue. I had to add the using statement for System.Linq.Dynamic.Core
Hope it helps someone else! :-)

Unit Test a file upload, how?

Using MVC3.NET I have a file upload method in a controller that works fine with the following signature public ActionResult UploadFile(IEnumerable<HttpPostedFileBase> file)
How can I unit test this with NUnit? I have looked around and everyone seems to point to Moq but I'm new to unit testing and cannot get Moq working.
I have found interesting blogs such as this: http://danielglyde.blogspot.com/2011/07/tdd-with-aspnet-mvc-3-moq-and.html but am struggling to figure out how the same might be done to 'fake' a file upload, and am also wary that a lot on moq examples that I have managed to find now seem to have deprecated code in them.
I would simply like to know how I can simulate a HttpPostedFileBase so I can test my upload code, using Moq or otherwise - I would be really grateful if someone could give me some code examples on how to do this.
The following code taken from other examples on here:
var file = new Mock<HttpPostedFileBase>();
file.Setup(f => f.ContentLength).Returns(1);
file.Setup(f => f.FileName).Returns("test.txt");
controller.upload(file);
generates the following error when I try to compile:
cannot convert from 'Moq.Mock' to
'System.Web.HttpPostedFileBase'
I have changed the method to take a singular HttpPostedFileBase for now, rather than an IEnumerable, as being able to 'mock' one is what I'm trying to focus on for the purpose of this question.
Assuming a standard file upload action:
[HttpPost]
public ActionResult UploadFile(IEnumerable<HttpPostedFileBase> files)
{
foreach (var file in files)
{
var filename = Path.Combine(Server.MapPath("~/app_data"), file.FileName);
file.SaveAs(filename);
}
return View();
}
you could test it like this:
[Test]
public void Upload_Action_Should_Store_Files_In_The_App_Data_Folder()
{
// arrange
var httpContextMock = new Mock<HttpContextBase>();
var serverMock = new Mock<HttpServerUtilityBase>();
serverMock.Setup(x => x.MapPath("~/app_data")).Returns(#"c:\work\app_data");
httpContextMock.Setup(x => x.Server).Returns(serverMock.Object);
var sut = new HomeController();
sut.ControllerContext = new ControllerContext(httpContextMock.Object, new RouteData(), sut);
var file1Mock = new Mock<HttpPostedFileBase>();
file1Mock.Setup(x => x.FileName).Returns("file1.pdf");
var file2Mock = new Mock<HttpPostedFileBase>();
file2Mock.Setup(x => x.FileName).Returns("file2.doc");
var files = new[] { file1Mock.Object, file2Mock.Object };
// act
var actual = sut.UploadFile(files);
// assert
file1Mock.Verify(x => x.SaveAs(#"c:\work\app_data\file1.pdf"));
file2Mock.Verify(x => x.SaveAs(#"c:\work\app_data\file2.doc"));
}
Obviously all the HttpContext setup part should be externalized into a reusable class that could be called in the [SetUp] phase of your unit test to prepare the mock context of the subject under test and to avoid repeating it in every single unit test.

Resources