In my class, I have a property for a file attachment like so...
public class Certificate {
[Required]
// TODO: Wow looks like there's a problem with using regex in MVC 4, this does not work!
[RegularExpression(#"^.*\.(xlsx|xls|XLSX|XLS)$", ErrorMessage = "Only Excel files (*.xls, *.xlsx) files are accepted")]
public string AttachmentTrace { get; set; }
}
I don't see anything wrong with my regex, but I always get ModelState.IsValid false. This seems pretty trivial and simple regex, am I missing something? Do I need to write my own custom validation?
I'm populating AttachmentTrace via a regular input of type file:
<div class="editor-label">
#Html.LabelFor(model => model.AttachmentTrace)
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.AttachmentTrace, new { type = "file" })
#Html.ValidationMessageFor(model => model.AttachmentTrace)
</div>
The action method is just a regular action:
public ActionResult Create(Certificate certificate, HttpPostedFileBase attachmentTrace, HttpPostedFileBase attachmentEmail)
{
if (ModelState.IsValid)
{
// code ...
}
return View(certificate);
}
Ok, here's the solution I found. I'm sure there are other solutions out there. First a little background, because my application uses EF code-first migration, specifying a HttpPostedFileBase property type in my model, produces this error when adding migration:
One or more validation errors were detected during model generation:
System.Data.Entity.Edm.EdmEntityType: : EntityType
'HttpPostedFileBase' has no key defined. Define the key for this
EntityType. \tSystem.Data.Entity.Edm.EdmEntitySet: EntityType:
EntitySet 'HttpPostedFileBases' is based on type 'HttpPostedFileBase'
that has no keys defined.
So I really had to stick with using a string type for the AttachmentTrace property.
The solution is to employ a ViewModel class like this:
public class CertificateViewModel {
// .. other properties
[Required]
[FileTypes("xls,xlsx")]
public HttpPostedFileBase AttachmentTrace { get; set; }
}
Then create a FileTypesAttribute like so, I borrowed this code from this excellent post.
public class FileTypesAttribute : ValidationAttribute {
private readonly List<string> _types;
public FileTypesAttribute(string types) {
_types = types.Split(',').ToList();
}
public override bool IsValid(object value) {
if (value == null) return true;
var postedFile = value as HttpPostedFileBase;
var fileExt = System.IO.Path.GetExtension(postedFile.FileName).Substring(1);
return _types.Contains(fileExt, StringComparer.OrdinalIgnoreCase);
}
public override string FormatErrorMessage(string name) {
return string.Format("Invalid file type. Only {0} are supported.", String.Join(", ", _types));
}
}
In the controller Action, I needed to make a change to use the ViewModel instead, then map it back to my Entity using AutoMapper (which is excellent by the way):
public ActionResult Create(CertificateViewModel certificate, HttpPostedFileBase attachmentTrace, HttpPostedFileBase attachmentEmail) {
if (ModelState.IsValid) {
// Let's use AutoMapper to map the ViewModel back to our Certificate Entity
// We also need to create a converter for type HttpPostedFileBase -> string
Mapper.CreateMap<HttpPostedFileBase, string>().ConvertUsing(new HttpPostedFileBaseTypeConverter());
Mapper.CreateMap<CreateCertificateViewModel, Certificate>();
Certificate myCert = Mapper.Map<CreateCertificateViewModel, Certificate>(certificate);
// other code ...
}
return View(myCert);
}
For the AutoMapper, I created my own TypeConverter for the HttpPostedFileBase as follows:
public class HttpPostedFileBaseTypeConverter : ITypeConverter<HttpPostedFileBase, string> {
public string Convert(ResolutionContext context) {
var fileBase = context.SourceValue as HttpPostedFileBase;
if (fileBase != null) {
return fileBase.FileName;
}
return null;
}
}
That's it. Hope this helps out others who may have this same issue.
Related
I can't verify and test my database by in memory providers.
for example I set these properties to required :
public abstract class Log
{
#region Properties
public Guid Id { get; set; }
[Required]
public string ClientIp { get; set; }
[Required]
public string Application { get; set; }
[Required]
public string Host { get; set; }
[Required]
public string Path { get; set; }
[Required]
public string Method { get; set; }
[Required]
public string User { get; set; }
[Required]
public string Date { get; set; }
#endregion
}
and this is my DBContext :
public class ApplicationDbContext : IdentityDbContext<ApplicationUsers, Role, Guid>, IUnitOfWork
{
private readonly IConfigurationRoot _configuration;
public ApplicationDbContext(IConfigurationRoot configuration)
{
_configuration = configuration;
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var useInMemoryDatabase = _configuration[key: "UseInMemoryDatabase"].Equals(value: "true",
comparisonType: StringComparison.OrdinalIgnoreCase);
if (useInMemoryDatabase)
optionsBuilder.UseInMemoryDatabase();
else
optionsBuilder.UseSqlServer(
connectionString: _configuration[key: "ConnectionStrings:ApplicationDbContextConnection"]
, sqlServerOptionsAction: serverDbContextOptionsBuilder =>
{
var minutes = (int) TimeSpan.FromMinutes(3).TotalSeconds;
serverDbContextOptionsBuilder.CommandTimeout(commandTimeout: minutes);
});
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Log>()
.HasKey(c => c.Id);
modelBuilder.Entity<Log>()
.HasDiscriminator<int>(name: "Type")
.HasValue<LogRequest>(value: Convert.ToInt32(value: LogLevel.Information))
.HasValue<LogError>(value: Convert.ToInt32(value: LogLevel.Error));
}
And this is my unit test :
[TestClass]
public class LogRepositoryTest
{
private readonly IServiceProvider _serviceProvider;
public LogRepositoryTest()
{
var services = new ServiceCollection();
services.AddScoped<IUnitOfWork, ApplicationDbContext>();
services.AddScoped<ILogRepository, LogRepository>();
services.AddSingleton(provider => new ConfigurationBuilder()
.AddInMemoryCollection(initialData: new[]
{
new KeyValuePair<string, string>(key: "UseInMemoryDatabase", value: "true"),
})
.Build());
services.AddEntityFrameworkInMemoryDatabase().AddDbContext<ApplicationDbContext>(ServiceLifetime.Scoped);
_serviceProvider = services.BuildServiceProvider();
}
[TestMethod]
public async Task Verify_SaveRequestLog()
{
using (var serviceScope = _serviceProvider.GetRequiredService<IServiceScopeFactory>().CreateScope())
{
using (var context = serviceScope.ServiceProvider.GetRequiredService<IUnitOfWork>())
{
context.Set<Log>().Add(new LogRequest());
var result =await context.SaveAllChangesAsync();
Assert.AreEqual(1, result);
}
}
}
But the unit test method always return 1 and passes, meanwhile the empty object of LogRequest must not save anything to database!
How can I determine not null properties for unit test ? In fact how can I enforce unit test to reflect to validation policies ?
Update:
Based on this linke :
Entity Framework Core Issues
that I asked, I got this respond:
EF Core doesn't do any validation of entities beyond what is needed
for internal consistency. Validation is something that could be done
in EF, but experience shows that it is not something that is useful to
many developers because it usually cannot replace either client-side
validation or database validation and there are also other places
where validation can be done more effectively.
Going beyond EF to the database, the in-memory database does not
currently validate nullability (i.e. requiredness) when saving
property values. I will leave this issue open so that we can discuss
as a team whether this is something we should add.
Also, if the intent is test with an in-memory database as an
approximation for a relational database, then you might want to
consider using SQLite in in-memory mode. See
https://learn.microsoft.com/en-us/ef/core/miscellaneous/testing/index
for more information.
Based on this linke :
Entity Framework Core Issues
that I asked, I got my answer :
class MyContext : DbContext
{
public override int SaveChanges()
{
var entities = from e in ChangeTracker.Entries()
where e.State == EntityState.Added
|| e.State == EntityState.Modified
select e.Entity;
foreach (var entity in entities)
{
var validationContext = new ValidationContext(entity);
Validator.ValidateObject(entity, validationContext);
}
return base.SaveChanges();
}
}
I am planning to write an ActionFilter for business validation and in which some services will be resolved via Service Locator(I know this is not good practice and as far as possible i avoid Service Locator pattern, but for this case i want to use it).
OnActionExecuting method of the filter is something like this:
public override void OnActionExecuting(ActionExecutingContext actionContext)
{
// get validator for input;
var validator = actionContext.HttpContext.RequestServices.GetService<IValidator<TypeOfInput>>();// i will ask another question for this line
if(!validator.IsValid(input))
{
//send errors
}
}
Is it possible to write unit test for above ActionFilterand how?
Here is an sample on how to create a mock (using XUnit and Moq framework) to verify that the IsValid method is called and where the mock returns an false.
using Dealz.Common.Web.Tests.Utils;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.DependencyInjection;
using Moq;
using System;
using Xunit;
namespace Dealz.Common.Web.Tests.ActionFilters
{
public class TestActionFilter
{
[Fact]
public void ActionFilterTest()
{
/****************
* Setup
****************/
// Create the userValidatorMock
var userValidatorMock = new Mock<IValidator<User>>();
userValidatorMock.Setup(validator => validator
// For any parameter passed to IsValid
.IsValid(It.IsAny<User>())
)
// return false when IsValid is called
.Returns(false)
// Make sure that `IsValid` is being called at least once or throw error
.Verifiable();
// If provider.GetService(typeof(IValidator<User>)) gets called,
// IValidator<User> mock will be returned
var serviceProviderMock = new Mock<IServiceProvider>();
serviceProviderMock.Setup(provider => provider.GetService(typeof(IValidator<User>)))
.Returns(userValidatorMock.Object);
// Mock the HttpContext to return a mockable
var httpContextMock = new Mock<HttpContext>();
httpContextMock.SetupGet(context => context.RequestServices)
.Returns(serviceProviderMock.Object);
var actionExecutingContext = HttpContextUtils.MockedActionExecutingContext(httpContextMock.Object, null);
/****************
* Act
****************/
var userValidator = new ValidationActionFilter<User>();
userValidator.OnActionExecuting(actionExecutingContext);
/****************
* Verify
****************/
// Make sure that IsValid is being called at least once, otherwise this throws an exception. This is a behavior test
userValidatorMock.Verify();
// TODO: Also Mock HttpContext.Response and return in it's Body proeprty a memory stream where
// your ActionFilter writes to and validate the input is what you desire.
}
}
class User
{
public string Username { get; set; }
}
class ValidationActionFilter<T> : IActionFilter where T : class, new()
{
public void OnActionExecuted(ActionExecutedContext context)
{
throw new NotImplementedException();
}
public void OnActionExecuting(ActionExecutingContext actionContext)
{
var type = typeof(IValidator<>).MakeGenericType(typeof(T));
var validator = (IValidator<T>)actionContext.HttpContext
.RequestServices.GetService<IValidator<T>>();
// Get your input somehow
T input = new T();
if (!validator.IsValid(input))
{
//send errors
actionContext.HttpContext.Response.WriteAsync("Error");
}
}
}
internal interface IValidator<T>
{
bool IsValid(T input);
}
}
HttpContextUtils.cs
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using System.Collections.Generic;
namespace Dealz.Common.Web.Tests.Utils
{
public class HttpContextUtils
{
public static ActionExecutingContext MockedActionExecutingContext(
HttpContext context,
IList<IFilterMetadata> filters,
IDictionary<string, object> actionArguments,
object controller
)
{
var actionContext = new ActionContext() { HttpContext = context };
return new ActionExecutingContext(actionContext, filters, actionArguments, controller);
}
public static ActionExecutingContext MockedActionExecutingContext(
HttpContext context,
object controller
)
{
return MockedActionExecutingContext(context, new List<IFilterMetadata>(), new Dictionary<string, object>(), controller);
}
}
}
As you can see, it's quite a mess, you need to create plenty of mocks to simulate different responses of the actuall classes, only to be able to test the ActionAttribute in isolation.
I like #Tseng's above answer but thought of giving one more answer as his answer covers more scenarios (like generics) and could be overwhelming for some users.
Here I have an action filter attribute which just checks the ModelState and short circuits(returns the response without the action being invoked) the request by setting the Result property on the context. Within the filter, I try to use the ServiceLocator pattern to get a logger to log some data(some might not like this but this is an example)
Filter
public class ValidationFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
if (!context.ModelState.IsValid)
{
var logger = context.HttpContext.RequestServices.GetRequiredService<ILogger<ValidationFilterAttribute>>();
logger.LogWarning("some message here");
context.Result = new JsonResult(new InvalidData() { Message = "some messgae here" })
{
StatusCode = 400
};
}
}
}
public class InvalidData
{
public string Message { get; set; }
}
Unit Test
[Fact]
public void ValidationFilterAttributeTest_ModelStateErrors_ResultInBadRequestResult()
{
// Arrange
var serviceProviderMock = new Mock<IServiceProvider>();
serviceProviderMock
.Setup(serviceProvider => serviceProvider.GetService(typeof(ILogger<ValidationFilterAttribute>)))
.Returns(Mock.Of<ILogger<ValidationFilterAttribute>>());
var httpContext = new DefaultHttpContext();
httpContext.RequestServices = serviceProviderMock.Object;
var actionContext = new ActionContext(httpContext, new RouteData(), new ActionDescriptor());
var actionExecutingContext = new ActionExecutingContext(
actionContext,
filters: new List<IFilterMetadata>(), // for majority of scenarios you need not worry about populating this parameter
actionArguments: new Dictionary<string, object>(), // if the filter uses this data, add some data to this dictionary
controller: null); // since the filter being tested here does not use the data from this parameter, just provide null
var validationFilter = new ValidationFilterAttribute();
// Act
// Add an erorr into model state on purpose to make it invalid
actionContext.ModelState.AddModelError("Age", "Age cannot be below 18 years.");
validationFilter.OnActionExecuting(actionExecutingContext);
// Assert
var jsonResult = Assert.IsType<JsonResult>(actionExecutingContext.Result);
Assert.Equal(400, jsonResult.StatusCode);
var invalidData = Assert.IsType<InvalidData>(jsonResult.Value);
Assert.Equal("some messgae here", invalidData.Message);
}
Hi I'm currently using Sitecore 8.1 update 1 with MVC5.2.3 and Glass Mapper. I'm having some issues with the Glass Mapper link field. I have created a template which is derived from Standard Rendering Parameters template in which I have used Sitecore General Link field. Also I have created Model for that:
My model:
[SitecoreType(TemplateId = "{912B074D-F8BA-4AA7-9276-016515A1ACE8}")]
public class RelatedArticleParams
{
[SitecoreId]
public virtual Guid Id { get; set; }
public virtual string HeaderText { get; set; }
[SitecoreField(FieldType = SitecoreFieldType.GeneralLink)]
public Link Link { get; set; }
}
My View:
#{
var parameters = GetRenderingParameters<RelatedArticleParams>();
}
#parameters.Text
Everything is fine if I add the link from presentation details at Sitecore backend. But when I click on this component at Sitecore Page Experience Editor and insert link to Rendering Parameters, then it will give An error occurred red line indication at top of the page. I cant insert link from Page Editor mode.
Please help me in this issue whether it is Glass Mapper bug or I am making any mistake ??
Thanks.
Will appreciate your suggestions.
Another option is to do it in code.
public class MyViewModel
{
public HtmlString MyLink { get; set; }
}
public class MyController : Controller
{
private readonly IGlassHtml _glassHtmlHelper;
public void MyController()
{
_glassHtmlHelper = new GlassHtml(new SitecoreContext());
}
public ViewResult MyControllerAction()
{
var viewModel = new MyViewModel();
//Get your item
viewModel.MyLink = new HtmlString(_glassHtmlHelper.RenderLink<RelatedArticleParams>(contentItem, x => x.Link, isEditable: true));
return View(viewModel);
}
}
Then, in your markup, all you have to do is:
#model MyViewModel
<div>
#Model.MyLink
</div>
You should use
#Editable(Property name of glass mapper) //using the Model property.
Reference
use
#RenderLink(x => x.Link)
or
#using (BeginRenderLink(x => x.GeneralLink, isEditable: true))
{
#RenderImage(x => x.Image)
}
http://glass.lu/Mapper/Sc/Tutorials/Tutorial22
I'm using Glass V4. I have a set up of MVC Web Area Project.
I have installed the Glass Mapper in the Main Project (WebProject).
I'm trying to do the Glass Casting in my Area Project.
public class ContactController : SitecoreController
{
private readonly ISitecoreContext _context;
private IGlassHtml _glassHtml;
public ContactController()
: this(new SitecoreContext())
{
}
public ContactController(ISitecoreContext context)
{
_context = context;
_glassHtml = new GlassHtml(context);
}
// GET: Contact
public ActionResult ContactUs()
{
var db = Sitecore.Context.Database;
var datasource = db.GetItem(RenderingContext.Current.Rendering.DataSource);
var ViewModel = new Models.ContactUs();
ViewModel.Headerstring = datasource.Fields["Headerstring"].Value;
ViewModel.Substring = datasource.Fields["Substring"].Value;
ViewModel.Description = ((MultilistField)datasource.Fields["Description"]).GetItems().Select(s => s.Fields["Line"].Value).ToList<string>();
return View(ViewModel);
}
public ActionResult ContactUsGlass()
{
var model = _context.GetCurrentItem<ContactUsGlassModel>();
return View(model);
}
}
I'm able to get the value with the First Action Method but not with the second.
Model:
public class ContactUs
{
public string Headerstring { get; set; }
public string Substring { get; set; }
public List<string> Description { get; set; }
}
Glass Model:
public class ContactUsGlassModel
{
public virtual string Headerstring { get; set; }
public virtual string Substring { get; set; }
}
I understand I don't need to register my Namespace in Glass V4.
You should not use _context.GetCurrentItem method. Use _context.GetItem instead:
public ActionResult ContactUsGlass()
{
var model = context.GetItem<ContactUsGlassModel>(RenderingContext.Current.Rendering.DataSource);
return View(model);
}
You don't want to get model from your Sitecore.Context.Item (which is used in GetCurrentItem method. You want to get your model from the DataSource of the current rendering.
What #Marek has answered is the right way of pulling the rendering item into model. GetCurrentItem by default gives the page item being served by Sitecore. If the fields that your model needs are fields of your page item then GetCurrentItem can also fill your model. If Datasource nesting is enabled, then if the datasource is not set on the rendering, Sitecore returns the page item again.
You can inherit from GlassController and then use GetLayoutItem() to get the datasorced item. If it's null then you need to publish the template in sitecore and make sure you mappings are correct if you are not using TDS :)
So I'm working with Umbraco 6.12 and having great difficulty been able to test a RenderMvcController.
I have implemented IApplicationEventHandler in my Global.ascx and Ninject is working fine and as expected when running the application - all good.
However, unit testing these controllers is a different matter. I found this, and have added the latest reply:
http://issues.umbraco.org/issue/U4-1717
I now have this lovely hack in my SetUp:
Umbraco.Web.UmbracoContext.EnsureContext(new HttpContextWrapper(new HttpContext(new HttpRequest("", "http://www.myserver.com", ""), new HttpResponse(null))), ApplicationContext.Current);
Which has got around the original UmbracoContext cannot be null, but is now throwing:
Current has not been initialized on Umbraco.Web.PublishedCache.PublishedCachesResolver. You must initialize Current before trying to read it.
The published caches resolver also seems to be hidden behind internal and protected stuff, which I can't use reflection to hack at as I can't init anything to pass into SetProperty reflection.
It's really frustrating, I'm loving v6, and using uMapper is very nice. I can inject a repo, service, command or query at will into the controllers and life is good - I just can't cover the controllers!
Any help on this would be greatly appreciated.
Thanks.
To unit test a Umbraco RenderMvcController, you need to grab the source code from github, compile the solution yourself, and get the Umbraco.Tests.dll and reference it on your test project.
In addition to that, you need to reference the SQLCE4Umbraco.dll which is distributed with the Umbraco packages, and Rhino.Mocks.dll which is internally for mocking.
To help you with this, I have compiled put the Umbraco.Tests.dll for Umbraco 6.1.5 and put it together with the Rhino.Mocks.dll and put it on this zip file.
Finally, derive your test from BaseRoutingTest, override the DatabaseTestBehavior to
NoDatabasePerFixture, and get the UmbracoContext and HttpBaseContext by calling the GetRoutingContext method, as in the code below:
using System;
using Moq;
using NUnit.Framework;
using System.Globalization;
using System.Web.Mvc;
using System.Web.Routing;
using Umbraco.Core.Models;
using Umbraco.Tests.TestHelpers;
using Umbraco.Web;
using Umbraco.Web.Models;
using Umbraco.Web.Mvc;
namespace UnitTests.Controllers
{
public class Entry
{
public int Id { get; set; }
public string Url { get; set; }
public string Title { get; set; }
public string Summary { get; set; }
public string Content { get; set; }
public string Author { get; set; }
public string[] Tags { get; set; }
public DateTime Date { get; set; }
}
public interface IBlogService
{
Entry GetBlogEntry(int id);
}
public class BlogEntryController : RenderMvcController
{
private readonly IBlogService _blogService;
public BlogEntryController(IBlogService blogService, UmbracoContext ctx)
: base(ctx)
{
_blogService = blogService;
}
public BlogEntryController(IBlogService blogService)
: this(blogService, UmbracoContext.Current)
{
}
public override ActionResult Index(RenderModel model)
{
var entry = _blogService.GetBlogEntry(model.Content.Id);
// Test will fail if we return CurrentTemplate(model) as is expecting
// the action from ControllerContext.RouteData.Values["action"]
return View("BlogEntry", entry);
}
}
[TestFixture]
public class RenderMvcControllerTests : BaseRoutingTest
{
protected override DatabaseBehavior DatabaseTestBehavior
{
get { return DatabaseBehavior.NoDatabasePerFixture; }
}
[Test]
public void CanGetIndex()
{
const int id = 1234;
var content = new Mock<IPublishedContent>();
content.Setup(c => c.Id).Returns(id);
var model = new RenderModel(content.Object, CultureInfo.InvariantCulture);
var blogService = new Mock<IBlogService>();
var entry = new Entry { Id = id };
blogService.Setup(s => s.GetBlogEntry(id)).Returns(entry);
var controller = GetBlogEntryController(blogService.Object);
var result = (ViewResult)controller.Index(model);
blogService.Verify(s => s.GetBlogEntry(id), Times.Once());
Assert.IsNotNull(result);
Assert.IsAssignableFrom<Entry>(result.Model);
}
private BlogEntryController GetBlogEntryController(IBlogService blogService)
{
var routingContext = GetRoutingContext("/test");
var umbracoContext = routingContext.UmbracoContext;
var contextBase = umbracoContext.HttpContext;
var controller = new BlogEntryController(blogService, umbracoContext);
controller.ControllerContext = new ControllerContext(contextBase, new RouteData(), controller);
controller.Url = new UrlHelper(new RequestContext(contextBase, new RouteData()), new RouteCollection());
return controller;
}
}
}
This code has only been tested in Umbraco 6.1.5.
According to the core team, you should include the Umbraco.Tests library and inherit your test from BaseUmbracoApplicationTest. That will setup a valid UmbracoApplication and UmbracoContext.
https://groups.google.com/forum/?fromgroups=#!topic/umbraco-dev/vEjdzjqmtsU
I have raised this on the Umbraco forums and there are several replies which may help you.
See here:
http://our.umbraco.org/forum/developers/api-questions/37255-How-can-I-unit-test-a-class-inheriting-from-SurfaceController
Essentially, you can .. just ... but requires some reflection because some of the key classes and interfaces are internal. As Luke's last post points out, this is because the functionality is currently a bit of a moving target.