Simple Service Mocking

9 posts, 0 answers
  1. Stacey
    Stacey avatar
    14 posts
    Member since:
    Feb 2011

    Posted 14 Apr 2011 Link to this post

    I have a simple 'Service' system set up with an interface as shown below. I am trying to mock it for use in my unit testing, but am having a bit of an obstacle. The way it works is that I design classes that implement IRequestFor<T,R> and I would call the service bus like this...

    var member = new Member { Name = "valid@email.com", Password = "validPassword" };ServiceBus.Query<ValidateUser>().With(member);

    This works fine in my code. I have no issues with it. But when I try to mock it, like this ..

    var service = Mock.Create<IServiceBus>();

               
    // Model
               
    var model = new Web.Models.Membership.Login
               
    {
                   
    Email = "acceptible@email.com",
                   
    Password = "acceptiblePassword",
                   
    RememberMe = true
               
    };

               
    // Arrange
               
    Mock.Arrange(() => service.Query<Membership.Messages.ValidateMember>().With(model))
                   
    .Returns(true);

    I am given the following error.

    NullReferenceException

    I don't even know what the exception is on. It 'points' to the ServiceBus in my Controller code, and if I use the debugger, the object is like .. {IServiceBus_Proxy_2718486e043f432da4b143c257cef8ce}, but other than that, everything else looks the exact same as if I step through it in a normal run.

    I am using Telerik JustMock for the mocking, but I don't know how I would do this in a different mocking framework either. I am using Ninject for my Dependency Injection, as well. Can anyone help me?

    For convenience, I have included as much of my code as possible below.

    Code Reference

    Service Bus

    public interface IServiceBus
    {
        T
    Query<T>() where T : IRequest;
        T
    Dispatch<T>() where T : IDispatch;
    }

    public interface IRequest
    {
    }

    public interface IDispatch
    {

    }

    public interface IRequestFor<TResult> : IRequest
    {
       
    TResult Reply();
    }

    public interface IRequestFor<TParameters, TResult> : IRequest
    {
       
    TResult With(TParameters parameters);
    }

    public interface IDispatchFor<TParameters> : IDispatch
    {
       
    void Using(TParameters parameters);
    }

    Service Bus Implementation

    public class ServiceBus : IServiceBus
    {
       
    private readonly IKernel kernel;

       
    public ServiceBus(IKernel kernel) {
           
    this.kernel = kernel;
       
    }

       
    /// <summary>
       
    /// Request a query behavior that may be given parameters to yield a result.
       
    /// </summary>
       
    /// <typeparam name="T">The type of query to request.</typeparam>
       
    /// <returns></returns>
       
    public T Query<T>() where T : IRequest
       
    {
           
    // return a simple injected instance of the query.
           
    return kernel.Get<T>();
       
    }

       
    /// <summary>
       
    /// Request a dispatch handler for a given query that may be given parameters to send.
       
    /// </summary>
       
    /// <typeparam name="T">The type of handler to dispatch.</typeparam>
       
    /// <returns></returns>
       
    public T Dispatch<T>() where T : IDispatch
       
    {
           
    // return a simple injected instance of the dispatcher.
           
    return kernel.Get<T>();
       
    }
    }

    Service Bus Dependency Injection Wiring (Ninject)

    Bind<IServiceBus>()
                   
    .To<ServiceBus>()
                   
    .InSingletonScope();

    Complete Unit Test

        [TestMethod]
       
    public void Login_Post_ReturnsRedirectOnSuccess()
       
    {
           
    // Inject
           
    var service = Mock.Create<IServiceBus>();
           
    var authenticationService = Mock.Create<System.Web.Security.IFormsAuthenticationService>();

           
    // Arrange
           
    var controller = new Web.Controllers.MembershipController(
                service
    , authenticationService
           
    );

           
    var httpContext = Mock.Create<HttpContextBase>();

           
    // Arrange
           
    var requestContext = new RequestContext(
               
    new MockHttpContext(),
               
    new RouteData());

            controller
    .Url = new UrlHelper(
                requestContext
           
    );

           
    // Model
           
    var model = new Web.Models.Membership.Login
           
    {
               
    Email = "acceptible@email.com",
               
    Password = "acceptiblePassword",
               
    RememberMe = true
           
    };

           
    // Arrange
           
    Mock.Arrange(() => service.Query<Membership.Messages.ValidateMember>().With(model))
               
    .Returns(true);

           
    // Act
           
    var result = controller.Login(model, "/Home/");

           
    // Assert
           
    Assert.IsInstanceOfType(result, typeof(RedirectResult));
       
    }

    Actual Query Method

    public class ValidateMember : IRequestFor<IValidateMemberParameters, bool>
    {
       
    private readonly ISession session;

       
    public ValidateMember(ISession session) {
           
    this.session = session;
       
    }

       
    public bool With(IValidateMemberParameters model)
       
    {
           
    if (String.IsNullOrEmpty(model.Email)) throw new ArgumentException("Value cannot be null or empty.", "email");
           
    if (String.IsNullOrEmpty(model.Password)) throw new ArgumentException("Value cannot be null or empty.", "password");

           
    // determine if the credentials entered can be matched in the database.
           
    var member = session.Query<Member>()
               
    .Where(context => context.Email == model.Email)
               
    .Take(1).SingleOrDefault();

           
    // if a member was discovered, verify their password credentials
           
    if( member != null )
               
    return System.Security.Cryptography.Hashing.VerifyHash(model.Password, "SHA512", member.Password);

           
    // if we reached this point, the password could not be properly matched and there was an error.
           
    return false;
       
    }
    }

  2. Ricky
    Admin
    Ricky avatar
    467 posts

    Posted 21 Apr 2011 Link to this post

    Hi Stacey,
    Thanks again for your detailed post. However, I took your sample and wrote it in a bit simplified way to better replicate the issue.

    Here, I think that you need to include an additional line that sets service.Query<ValidateMember> to return specific ValidateMember instance and then mock its member With(model) for your expected value:

    var session = Mock.Create<ISession>();
    var validateMember = Mock.Create<ValidateMember>(session);
    Mock.Arrange(() => service.Query<ValidateMember>()).Returns(validateMember);
     
    Mock.Arrange(()=> validateMember.With(model)).Returns(true);

    You are basically getting the null reference exception during service.Query<ValidateMember> call. Since, nested mocking works with method calls or properties returning instance type with default constructor. Therefore, it is returning null in this regard.  But this is a good issue to ponder and it will be great to include support for nested mocking of instance type with non default constructor as well. I have further created an PITS item that you can follow for progress:

    http://www.telerik.com/support/pits.aspx#/public/justmock/5664


    Finally, hope that the above solution works for you and i am also attaching my sample project to let you have a look. Please write us back if you still having problems.


    Kind Regards,
    Mehfuz
    the Telerik team
    Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items
  3. DevCraft R3 2016 release webinar banner
  4. Stacey
    Stacey avatar
    14 posts
    Member since:
    Feb 2011

    Posted 21 Apr 2011 Link to this post

    This still does not work. The ISession throws an exception when it gets called in the 'With' method. I've even tried mocking the session.Query<Member>() method call and it does not work.
  5. Ricky
    Admin
    Ricky avatar
    467 posts

    Posted 22 Apr 2011 Link to this post

    Hi Stacey,

    Thanks again for your reply. However, can you please try the sample i have sent and let me know what exception you are having for that? This will help me assist you with a better solution.  Here to note that i am using the latest JustMock build from NuGet (you can find the same from downloads).

    Sorry for the inconvenience.

    Kind regards,
    Mehfuz
    the Telerik team

    Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items
  6. Stacey
    Stacey avatar
    14 posts
    Member since:
    Feb 2011

    Posted 22 Apr 2011 Link to this post

    Your example project works fine, however it does not appropriately demonstrate the problem I am having. 

    In the ValidateMember class, your 'With' method does not call upon ISession. Mine looks like this ..

    public bool With(IValidateMemberParameters model)
       
    {
           
    if (String.IsNullOrEmpty(model.Email)) throw new ArgumentException("Value cannot be null or empty.", "email");
           
    if (String.IsNullOrEmpty(model.Password)) throw new ArgumentException("Value cannot be null or empty.", "password");

           
    // determine if the credentials entered can be matched in the database.
           
    var member = session.Query<Member>()
               
    .Where(context => context.Email == model.Email)
               
    .Take(1).SingleOrDefault();

           
    // if a member was discovered, verify their password credentials
           
    if( member != null )
               
    return System.Security.Cryptography.Hashing.VerifyHash(model.Password, "SHA512", member.Password);

           
    // if we reached this point, the password could not be properly matched and there was an error.
           
    return false;
       
    }

    when session.Query<Member>().Where(context => context.Email == model.Email).Take(1).SingleOrDefault(); is called, I get a NullReferenceException

    This occurs even when using your proposed method of mocking ISession through the ValidateMember constructor. 

    It is my understanding that the actual method should never be called, but for whatever reason it is. Your example project works fine, but it does not have the same code. The ISession is from nHibernate

    I have a lot more code of my project posted here : http://stackoverflow.com/questions/5663435/mocking-a-simple-service-bus-in-asp-net-mvc
    But I can add it here for your convenience.

    Basically I am down to the problem of ... if I take the session.Query<Member> out of my actual code, the test will pass, but if I leave my code intact, the test will always fail.

    Login Controller Action

        [ValidateAntiForgeryToken] 
       
    [HttpPost]
       
    public ActionResult Login(Web.Models.Membership.Login model, string returnUrl)
       
    {
           
    if (ModelState.IsValid)
           
    {
               
    // attempt to validate the user, and if successful, pass their credentials to the
               
    // forms authentication provider.
               
    if (Bus.Query<ValidateMember>().With(model))
               
    {
                   
    // retrieve the authenticated member so that it can be passed on
                   
    // to the authentication service, and logging can occur with the
                   
    // login.
                   
    Authentication.SignIn(model.Email, model.RememberMe);

                   
    if (Url.IsLocalUrl(returnUrl))
                       
    return Redirect(returnUrl);
                   
    else
                       
    return RedirectToAction("Index", "Home");
               
    }
               
    else
               
    {
                   
    ModelState.AddModelError("", "The user name or password provided is incorrect.");
               
    }
           
    }

           
    // If we got this far, something failed, redisplay form
           
    return View(model);
       
    }

    Login View Model

    public class Login : Membership.Messages.IValidateMemberParameters
    {
       
    [Required]
       
    [DataType(DataType.EmailAddress)]
       
    [RegularExpression(@"^[a-z0-9_\+-]+(\.[a-z0-9_\+-]+)*@(?:[a-z0-9-]+){1}(\.[a-z0-9-]+)*\.([a-z]{2,})$", ErrorMessage = "Invalid Email Address")]
       
    [Display(Name = "Email Address")]
       
    public string Email { get; set; }

       
    [Required]
       
    [StringLength(32, MinimumLength = 6)]
       
    [DataType(DataType.Password)]
       
    [RegularExpression(@"^([a-zA-Z0-9@#$%]){6,32}$", ErrorMessage = "Invalid Password. Passwords must be between 6 and 32 characters, may contain any alphanumeric character and the symbols @#$% only.")]
       
    [Display(Name = "Password")]
       
    public string Password { get; set; }

       
    [Display(Name = "Remember me?")]
       
    public bool RememberMe { get; set; }
    }


  7. Stacey
    Stacey avatar
    14 posts
    Member since:
    Feb 2011

    Posted 25 Apr 2011 Link to this post

    Still the only way I can get a test to pass is to remove my ISession.Query<T> entirely from my real code - - and from my understanding, it isn't good unit testing if you have to change your original code to make it work. I've tried .DoNothing(), .DoInstead(), etc. None of it seems to make any difference... 
  8. Ricky
    Admin
    Ricky avatar
    467 posts

    Posted 28 Apr 2011 Link to this post

    Hi Stacey,
    Thanks again for your reply. However, if you are using the free version and as it does not support profiling the following test code will fail:

    var session = Mock.Create<ISession>();
    var validateMember = Mock.Create<ValidateMember>(session);
    Mock.Arrange(() => service.Query<ValidateMember>()).Returns(validateMember);
      
    Mock.Arrange(()=> validateMember.With(model)).Returns(true);

    In other words, With is a final method and without the profiler JustMock can't intercept the call to return expected "true".  Therefore, it executes the original code block and fails on the ISession.Query<> , since it uses a LINQ query (IQueryable) and expecting a collection (while can  also be mocked using the commercial edition).

    But good news is that all your target methods are implemented from its corresponding interface, therefore you could write the test in the following way:

    var validateMember = Mock.Create<IRequestFor<IValidateMemberParameters, bool>>();
    Mock.Arrange(() => service.Query<IRequestFor<IValidateMemberParameters, bool>>()).Returns(validateMember);
    Mock.Arrange(()=> validateMember.With(model)).Returns(true);
     
    Assert.IsTrue(service.Query<IRequestFor<IValidateMemberParameters, bool>>().With(model));


    This will make validateMemeber.With(model) to return the expected value and pass the test. Hope this solves your issue.


    Should you have anymore questions , please don't hesitate to write us back.

    Kind Regards,
    Mehfuz
    the Telerik team
    Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items
  9. Stacey
    Stacey avatar
    14 posts
    Member since:
    Feb 2011

    Posted 09 Jun 2011 Link to this post

    I have downloaded a trial of the full version, and the query still does not work. Do I need to do something 'special' to enable this 'profiling' feature you speak of?
  10. Ricky
    Admin
    Ricky avatar
    467 posts

    Posted 15 Jun 2011 Link to this post

    Hi Stacey,
    Thanks for the reply. If you have checked "Integrate with visual studio" option during installation then it should have automatically registered the profiler for you. In order to check if the profiler is working correctly, you can check this line in your test method:


    Assert.IsTrue(Mock.IsProfilerEnabled);

    Optionally, please make sure that your trial has not expired.

    Kind Regards,
    Mehfuz
    the Telerik team
    Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items
Back to Top
DevCraft R3 2016 release webinar banner