Saturday, December 21, 2013

Custom Model Validation Provider for enabling ASP.NET MVC 3 client side validation.


Implementing Custom Model Validation Provider to enable client side MVC 3 validation 

The Validation framework of ASP.NET MVC 3 is designed for extensibility and customization.  It supports validation model using attributes, or by implementing IValidatableObject or unobtrusive javascript based client side validation. It supports both property based or model based validation.

But, again ASP.Net MVC 3 assumes your solution has view models defined in a simple, flat & non hierarchical fashion to make things work. Sometimes, in applications we need to apply or perform validation rather in different ways to comply with existing legacy code, architectural style and culture.

Update: Following solution is applicable to ASP.NET MVC 3 FRAMEWORK ON VS 2010

One last word!! we assume you have in place all necessary jquery /javascript /css files for the MVC client side validation to work

Use case for Implementing Custom ModelValidatorProvider:

In my case, I did not have the pleasure to have view models exclusively defined for the mvc project.

Domain model classes are directly applied to MVC views. I cannot litter domain classes with validation attributes. They are shared components. They derive from some base classes. So, I couldn't really force annotation based validation working directly using this convoluted & hierarchical model classes as shown below:





















Binding JuniorEngineer model to mvc view and enabling property level client side validation declaratively using attributes/annotations seemed difficult for me.

Inject the validation attributes at Run time on the model:

So, we decided to inject validation attributes on model at the run time dynamically. In this case, we have to go for implementing custom validation solution which means we have to create a custom ModelValidatorProvider that allows to inject validation rules on the fly into the model.

Implementing Custom DataAnnotationsModelValidatorProvider:

MVC provides hook to plug in your custom implementation of ModelValidatorProvider. The purpose is to bind validation attributes dynamically to model to enable client side validation.

 public class JuniorEmployeeModelValidator : DataAnnotationsModelValidatorProvider
    {
        protected override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context, IEnumerable<Attribute> attributes)
        {
            if (metadata.ContainerType != typeof(JuniorEngineer))
                return base.GetValidators(metadata, context, attributes);
            //enable client side validation for name prop for junior engineer model. Name property is inherited from base abstract employee class.
            if (!string.IsNullOrWhiteSpace(metadata.PropertyName) && metadata.PropertyName == "Name")
            {
                //injecting required attribute to name /salary properties.
                attributes = new List<Attribute>()
                {
                    new RequiredAttribute()
                    {
                        ErrorMessage = "Hey babe, we just need a human name."
                    }
                };
            }
            if (!string.IsNullOrWhiteSpace(metadata.PropertyName) && metadata.PropertyName == "Salary")
            {
                attributes = new List<Attribute>()
                {
                    new RequiredAttribute()
                    {
                        ErrorMessage = "No one works for free!! Give him some money."
                    }
                };
            }
            return base.GetValidators(metadata, context, attributes);
        }

      
    }




Register in Global.asax:
Any customization or extension should be registered. Or You get no cookie!!












Saturday, December 14, 2013

Generic TryParse handler for generic .Net Lists



Following C# code sample is a Generic TryParse handler for generic value type 
lists in .Net.  Any character delimited list can be split and use safe TryParse against split list 
to ensure you have valid data.




using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace GenericTryParsehandler
{
    /// <summary>
    /// Generic TryParsehandler to split and parse generic value type lists.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="value"></param>
    /// <param name="result"></param>
    /// <returns></returns>
    public delegate bool TryParseHandler<T>(string value, out T result);
    class Program
    {
        static void Main(string[] args)
        {
            var semiColonDelimitedDateInput = "01/01/2001;2/02/2012;05/31/2013;05/05/1992";
            var commaDelimitedIntInput = "3,4,5,6";
            var intresult = commaDelimitedIntInput.TryParseList<int>(new[] { ',' }, int.TryParse);
            var dateresult = semiColonDelimitedDateInput.TryParseList<DateTime>(new[] { ';' }, DateTime.TryParse);
            
            if (dateresult.Count > 0)
            {
                foreach (var element in dateresult)
                {
                    Console.WriteLine(element);
                }
            }
            else
            {
                Console.WriteLine("Your semiColonDelimitedDateInput cannot be parsed.");
            }
            Console.WriteLine("*******************************************************************");
            if (intresult.Count > 0)
            {
                foreach (var element in intresult)
                {
                    Console.WriteLine(element);
                }
            }
            else
            {
                Console.WriteLine("Your commaDelimitedIntInput cannot be parsed.");
            }
            Console.ReadLine();
        }
    }


    /// <summary>
    /// 
    /// </summary>

    public static class ExtensionMethods
{
        /// <summary>
        /// Generic handler to split and TryParse each item in the list. Handler supports value types.. 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <param name="separator"></param>
        /// <param name="handler"></param>
        /// <returns></returns>
    public static List<T> TryParseList<T>(this string source, char[] separator, TryParseHandler<T> handler)
        where T : struct

    {
        var splitList = new List<T>();
        if (string.IsNullOrEmpty(source)) return splitList;

        var parsedString = source.Split(separator, StringSplitOptions.RemoveEmptyEntries).ToList();

        parsedString.ForEach(x =>
        {
            T result;
            if (handler(x, out result))
            {
                splitList.Add(result);
            }
        });

        return splitList.Count == parsedString.Count ? splitList : new List<T>();
    }
}
}

10 Practical ASP.Net MVC 4 Interview Questions



10  Practical ASP.Net MVC 4 Interview Questions:


1)  Do you think following is valid route definition for ASP.NET MVC solution?






Answer: YES. Variable length routes are valid. This route will match any URL, irrespective of number of placeholders it contains.

_________________________________________________________________________________

2)  Is the following route definition a valid one? If so please explain? 




Answer: This route is constrained using Regular expression. Only numeric values are accepted for the id parameter 

_________________________________________________________________

3)  What is the primary difference between these two Html Helpers for Validation?





Answer: Latter displays only model level validation errors NOT the property level errors. Former displays both model and property level errors

__________________________________________________________

4)   Does this Action Method compiles and redirects the user to another view?

Answer: YES. It is a valid redirect.

_______________________________________________________________________

5) What is the difference between these Html Helpers?



Answer: Latter is destroyed after you read it while former preserves it for next request.

___________________________________________________________

6) What is the difference between two techniques? Which one to prefer?











Answer:  They are one and same. We prefer second approach. It is built in helper method for controller to return 404 code

________________________________________________________________________

7) Can you override MVC convention and define controller name without ending in "Controller" as shown below?







Answer: YES. We can override default MVC convention to define controller name with out ending in "Controller". But, we have to implement our own custom controller factory.

________________________________________________________________________

8) What the result of this view? Will it be a runtime exception or just works fine?








Answer: Session Disabled controller has no impact on ViewBag. View Just renders fine with message: "My Controller has no session".

________________________________________________________________________

9) What is difference between @Url.Action and @Url.RouteUrl?











Answer: Semantically, they are same. 
  •  Url.RouteUrl allows you to specify a particular route by name. 
  • Url.Action will simply pick the first route that matches the criteria.
________________________________________________________________________________

10) List out filters available in MVC world if you can please specify their order of precedence?

Answer: Exception filter can kick in at any time.
  1.  Authorization filter
  2. Action filter
  3. Result filter
  4. Exception filter

Sunday, December 8, 2013

Automated asp.net web application testing using Selenium


ASP.Net Web Application Integration testing using Selenium 2.0


Selenium primarily used for automating web applications for testing purposes. You can create browser based regression tests using Selenium WebDriver. 

Selenium WebDriver provides a concise programming interface. It drives the browser much more effectively. Selenium WebDriver makes direct calls to browser using each browser's native support for automation. 

This articles deals with running tests against the application all running on the same machine. WebDriver will run the browser directly. *This article doesn't cover test distribution over multiple machine*. The article covers running tests for IE browser.

Setup:

To create and run Selenium tests using IE or chrome browser, we need to set up and reference few things before we hit the ground running.

Note: Following instructions are specific to IE browser.

DriverServer Setup:

1) Based on your computer architecture, please download 32bit or 64 bit IEDriverServer from here.
2) UnZip the contents of download and you can run the Driver in the two ways
                       a) Designate Driver.exe as part of your Test project test settings as deployable item so it                                    copied to test project output every time you run the tests
                        b) Instead, set the IEDriverServer.Exe path as part of PATH environment variable.


 3) The point is IEDriverServer should be running when executing tests using selenium

Assembly References in VS2012:

1) Please download the latest version of Selenium-dotnet-(version).zip which includes .Net bindings and            WebDriver programming API from here
2) UnZip the contents of download and reference the "WebDriver.dll" based on your target .net Framework      to your testing project as shown below.


3) You're all set now

Running the tests:

Following are the simple tests designed for a pseudo website with two pages. First test automates the login function and second test automates user details on the home page.

When you run the tests, you'll see IEDriverServer console running up and your sample site being run by Selenium in the web browser as shown below:


    first test results


 second test results

Code Sample:


using System;
using System.ComponentModel;
using System.Data;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenQA.Selenium;
using OpenQA.Selenium.IE;


namespace SeleniumAutomatedTests
{
    [TestClass]
    public class SeleniumUnitTests
    {
        private static InternetExplorerDriver _webDriver;

        [TestMethod]
        public void TestSiteLoginFunction()
        {
            //initialize internet explorer driver and you can set up options for driver 
            _webDriver = new InternetExplorerDriver(new InternetExplorerOptions()
            {
                InitialBrowserUrl = "http://localhost/TestSite/Login.aspx",
                
            });
            //find the desired elements by id, css class or name, xpath 
            _webDriver.FindElement((By.Id("userNameTxtBox"))).SendKeys("Selenium");
            _webDriver.FindElement(By.Id("passwordTxtBox")).SendKeys("selenium");

            var logInButton = _webDriver.FindElement(By.Id("signInBtn"));
            //simulating the button click
            logInButton.Click();

            //wait for 5 seconds for the page to load
            _webDriver.Manage().Timeouts().ImplicitlyWait(new TimeSpan(0, 0, 5));

            var welcomeLabel = _webDriver.FindElement(By.Id("WelcomeLabel"));
            //interface to execute javascript code on the host page
            var js = (IJavaScriptExecutor) _webDriver;
            //you can also execute on fly javascript code or predefined javascript functions attached to host page.
            js.ExecuteScript("alert('you successfully signed in.')");
            //finally, some asserting
            Assert.IsNotNull(welcomeLabel.Text);
           
        }

        [TestMethod]
        public void TestHomePageSubmitFunction()
        {
            _webDriver = new InternetExplorerDriver();
            _webDriver.Navigate().GoToUrl("http://localhost/TestSite/Home.aspx");
            _webDriver.FindElement((By.Id("ageTxtBox"))).SendKeys("29");
            _webDriver.FindElement(By.Id("cityTxtBox")).SendKeys("Los Angeles");
            //submitting the form using selenium
            var form = _webDriver.FindElement(By.Id("detailsForm"));
           form.Submit();

            _webDriver.Manage().Timeouts().ImplicitlyWait(new TimeSpan(0, 0, 5));
            var welcomeLabel = _webDriver.FindElement(By.Id("WelcomeLabel"));
            Assert.IsTrue(welcomeLabel.Text.Contains("Los Angeles") && welcomeLabel.Text.Contains("29"));
        }


    }

   
}

Hope this helps..