Using moq to verify a method is called in a repository - asp.net

I asked this question earlier about testing a controller action and verifying that a method in my repository was being called. The answer came back that I should be testing a Save method which is called inside the Register method (both in the same repository) in a seperate test on the repository only. That's what I thought, but I'm coming to do the test and I can't get it to work. :(
Here's the repository test, where am I going wrong?
[TestMethod]
public void Register_calls_Save_method_when_Member_is_valid()
{
_mockMemberRepository.Setup(r => r.GetByEmail(It.IsAny<string>())).Returns((Member)null);
MembershipCreateStatus status = _mockMemberRepository.Object.Register(_testMember.Email, "password", "password");
_mockMemberRepository.Verify(r => r.Save(It.IsAny<Member>()), Times.Once());
}
Here's the Register method on the repository:
public MembershipCreateStatus Register(string email, string password, string confirm)
{
if (password.Equals(confirm))
{
try
{
Member m = GetByEmail(email);
if (m == null)
{
int format = (int)PasswordFormatEnum.Encrypted;
string salt = GenerateSalt();
string pass = EncodePassword(password, format, salt);
m = new Member()
{
Email = email,
Password = pass,
PasswordSalt = salt,
PasswordFormat = format
};
Save(m);
return MembershipCreateStatus.Success;
}
else
return MembershipCreateStatus.DuplicateEmail;
//"A user with that email address already exists. Please use the Forgotten Password link if you need to recover your password.";
}
catch (Exception ex)
{
_logger.LogError(ex);
return MembershipCreateStatus.ProviderError;
}
}
return MembershipCreateStatus.InvalidPassword;
}

You can't use Moq to verify that you're calling one method on an object from another method on that object. What you can do is verify that something that is called in your Save() method is called.
For example, if I was writing my own repository that was using Ado.Net to update a database I could do something like the following:
public class MyRepository : IRepository {
private readonly IDatabase m_db;
public MyRepository(IDatabase myDatabase){
m_db = myDatabase;
}
public void Register(string email, string password, etc.){
// ... do stuff ...
Save(m);
// ... do stuff ...
}
public void Save(Member member){
// ... build sql query ...
m_db.ExecuteNonQuery(sqlCommand);
}
}
You'd then pass a mocked database object to your repository in your test and you'd verify that:
[TestMethod]
public void Register_calls_Save_method_when_Member_is_valid()
{
Mock<IDatabase> _mockDB = new Mock<IDatabase>();
// Setup mockDB with return values for GetByEmail(), etc.
_repository = new MyRepository(_mockDB.Object);
MembershipCreateStatus status = _repository.Register("Email#Email.com", "password", "password");
_mockDB.Verify(r => r.ExecuteNonQuery(It.IsAny<SqlCommand>()), Times.Once());
}
So, you're not verifying that Save() is called explicitly, but by verifying that the right underlying database call was called you can verify that Save() happened.
The same approach should work for other frameworks too.

Related

Mocking Services in ASP.NET Core

I have a simple form to save and then use MailKit to provide email notification, with xUnit and Moq used for unit testing. I'm having difficulty setting up the unit test and associated services. I have a workaround ('if' statement in the action method) to only test the core repo saving functionality without also testing the email service. If I take out the if statement, the unit test does not have access to the appropriate methods, such as setting the web root path. The error is a null exception. If I default this value, there are other errors, such as "no database provider being configured for DbContext."
Is there a more appropriate way to set a unit test of this sort up? Or is it wrong to set up a unit test to test both the Create() and email functionality because it violates the one-function unit testing rule?
Unit test:
[Fact]
public void Can_Create_New_Lesson()
{
//Arrange
//create a mock repository
Mock<IHostingEnvironment> mockEnv = new Mock<IHostingEnvironment>();
Mock<ILessonRepository> mockRepo = new Mock<ILessonRepository>();
Mock<UserManager<AppUser>> mockUsrMgr = GetMockUserManager();
Mock<RoleManager<IdentityRole>> mockRoleMgr = GetMockRoleManager();
var opts = new DbContextOptions<AppIdentityDbContext>();
Mock <AppIdentityDbContext> mockCtx = new Mock<AppIdentityDbContext>(opts);
//create mock temporary data
Mock<ITempDataDictionary> tempData = new Mock<ITempDataDictionary>();
//create the controller
LessonController target = new LessonController(mockRepo.Object, mockEnv.Object, mockUsrMgr.Object, mockRoleMgr.Object, mockCtx.Object)
{
TempData = tempData.Object
};
//create a lesson
Lesson lesson = new Lesson { Title = "Unit Test", Domain= "Unit Test"};
//Act
//try to save the product using the Create method of the controller
IActionResult result = target.Create(lesson);
//Assert
//check that the repository was called
mockRepo.Verify(m => m.SaveLesson(lesson));
//check the result type is a redirection to the List action method of the controller
Assert.IsType<RedirectToActionResult>(result);
Assert.Equal("Success", (result as RedirectToActionResult).ActionName);
}
The Create() action method:
[HttpPost]
public IActionResult Create(Lesson lesson)
{
if (ModelState.IsValid)
{
repository.SaveLesson(lesson);
//This IF statement is a workaround for the unit test
//don't email users if the Title is "Unit Test"
if (lesson.Title != "Unit Test")
{
emailUsers(lesson);
}
TempData["message"] = $"{lesson.Title} has been saved";
//show the user that the update was made successfully
return RedirectToAction("Success");
}
else
{
//there is a problem with the data values
return View(lesson);
}
}
Email function:
public void emailUsers(Lesson lesson)
{
var webRoot = environment.WebRootPath;
var filePath = System.IO.Path.Combine(webRoot, "email\\NewLessonSubmitted.txt");
string message = System.IO.File.ReadAllText(filePath);
string domain = lesson.Domain;
IQueryable<AppUser> userList = GetUsersInRole(identityContext, domain);
//if there are users in that domain, send the email
if (userList != null)
{
foreach (AppUser user in userList)
{
sendEmail(domain, message, user.Email);
}
}
}
EDIT: I've instead implemented the email service as a class, as pointed out by MotoSV. However, I'm still getting an error for "No database provider has been configured for this DbContext" The stack trace for the exception points to the following method:
public static IQueryable<AppUser> GetUsersInRole(AppIdentityDbContext db, string roleName)
{
if (db != null && roleName != null)
{
var roles = db.Roles.Where(r => r.Name == roleName);
if (roles.Any())
{
var roleId = roles.First().Id;
return from user in db.Users
where user.Roles.Any(r => r.RoleId == roleId)
select user;
}
}
return null;
}
I have this constructor in my dbContext class:
public AppIdentityDbContext(DbContextOptions<AppIdentityDbContext> options)
: base(options) { }
EDIT: The solution (provided by MotoSV) was to:
1) Create an email service class with appropriate methods and
2) Install the appropriate Nuget package for Microsoft.EntityFrameworkCore.InMemory
3) mock the DbContext as:
var opts = new DbContextOptionsBuilder<AppIdentityDbContext>()
.UseInMemoryDatabase(Guid.NewGuid().ToString())
.Options;
Mock<AppIdentityDbContext> mockCtx = new Mock<AppIdentityDbContext>(opts);
I would look at moving the code responsible for sending emails into it's own class. This class will implement an interface that can then be mocked in your test.
For example, create the interface and implementation:
public interface IEmailService
{
void SendEmail(string to, string from, string body);
}
public class EmailService : IEmailService
{
public void SendEmail(string to, string from string body)
{
...
}
}
The EmailService class will hold the functionality required to talk to MailKit. Then, register the IEmailService with .NET Core and add it to the constructor of your class:
public class LessonController : Controller
{
private readonly IEmailService _emailService;
public LessonController(IEmailService service, ...)
{
_emailService = emailService;
}
public void emailUsers(Lessong lesson)
{
...
if(userList != null)
{
foreach(...)
{
_emailService.Send(...);
}
}
...
}
}
In your test create a mock and pass that into your constructor.
First and foremost, you should never do stuff like putting in conditionals in your code for the purpose of unit testing. If for no other reason, you're violating the entire point of unit testing, as your test access different code paths than what your users actually experience; you learn nothing by doing this.
Testing that the repo actually saves is a job for a repo test not an action test. Likewise with your mail service: ensuring that an email is actually sent should be a test on your mail service, not your action method.
Long and short, your test here should simply ensure that the appropriate actions are taken (i.e. repo save is hit and email service send is hit). As such, you can drop in simple mocks that merely have those methods available to be hit. You don't need to (and shouldn't) be establishing full connections to the DB/SMTP server, as at that point you're integration testing, not unit testing.
Your applications send email class constructor should take an "email provider" object that is a generic email abstraction based on an IEmailProvider interface, and/or also take a IDataAccessProvider implementation.
Now you can mock both of these interfaces in the test and pass them to the send email class to test just your implementation.

SignalR - Sending a message to a specific user using (IUserIdProvider) *NEW 2.0.0*

In the latest version of Asp.Net SignalR, was added a new way of sending a message to a specific user, using the interface "IUserIdProvider".
public interface IUserIdProvider
{
string GetUserId(IRequest request);
}
public class MyHub : Hub
{
public void Send(string userId, string message)
{
Clients.User(userId).send(message);
}
}
My question is: How do I know to whom I am sending my message? The explanation of this new method is very superficial. And the draft Statement of SignalR 2.0.0 with this bug and does not compile. Has anyone implemented this feature?
More Info : http://www.asp.net/signalr/overview/signalr-20/hubs-api/mapping-users-to-connections#IUserIdProvider
Hugs.
SignalR provides ConnectionId for each connection. To find which connection belongs to whom (the user), we need to create a mapping between the connection and the user. This depends on how you identify a user in your application.
In SignalR 2.0, this is done by using the inbuilt IPrincipal.Identity.Name, which is the logged in user identifier as set during the ASP.NET authentication.
However, you may need to map the connection with the user using a different identifier instead of using the Identity.Name. For this purpose this new provider can be used with your custom implementation for mapping user with the connection.
Example of Mapping SignalR Users to Connections using IUserIdProvider
Lets assume our application uses a userId to identify each user. Now, we need to send message to a specific user. We have userId and message, but SignalR must also know the mapping between our userId and the connection.
To achieve this, first we need to create a new class which implements IUserIdProvider:
public class CustomUserIdProvider : IUserIdProvider
{
public string GetUserId(IRequest request)
{
// your logic to fetch a user identifier goes here.
// for example:
var userId = MyCustomUserClass.FindUserId(request.User.Identity.Name);
return userId.ToString();
}
}
The second step is to tell SignalR to use our CustomUserIdProvider instead of the default implementation. This can be done in the Startup.cs while initializing the hub configuration:
public class Startup
{
public void Configuration(IAppBuilder app)
{
var idProvider = new CustomUserIdProvider();
GlobalHost.DependencyResolver.Register(typeof(IUserIdProvider), () => idProvider);
// Any connection or hub wire up and configuration should go here
app.MapSignalR();
}
}
Now, you can send message to a specific user using his userId as mentioned in the documentation, like:
public class MyHub : Hub
{
public void Send(string userId, string message)
{
Clients.User(userId).send(message);
}
}
Here's a start.. Open to suggestions/improvements.
Server
public class ChatHub : Hub
{
public void SendChatMessage(string who, string message)
{
string name = Context.User.Identity.Name;
Clients.Group(name).addChatMessage(name, message);
Clients.Group("2#2.com").addChatMessage(name, message);
}
public override Task OnConnected()
{
string name = Context.User.Identity.Name;
Groups.Add(Context.ConnectionId, name);
return base.OnConnected();
}
}
JavaScript
(Notice how addChatMessage and sendChatMessage are also methods in the server code above)
$(function () {
// Declare a proxy to reference the hub.
var chat = $.connection.chatHub;
// Create a function that the hub can call to broadcast messages.
chat.client.addChatMessage = function (who, message) {
// Html encode display name and message.
var encodedName = $('<div />').text(who).html();
var encodedMsg = $('<div />').text(message).html();
// Add the message to the page.
$('#chat').append('<li><strong>' + encodedName
+ '</strong>: ' + encodedMsg + '</li>');
};
// Start the connection.
$.connection.hub.start().done(function () {
$('#sendmessage').click(function () {
// Call the Send method on the hub.
chat.server.sendChatMessage($('#displayname').val(), $('#message').val());
// Clear text box and reset focus for next comment.
$('#message').val('').focus();
});
});
});
Testing
This is how use SignarR in order to target a specific user (without using any provider):
private static ConcurrentDictionary<string, string> clients = new ConcurrentDictionary<string, string>();
public string Login(string username)
{
clients.TryAdd(Context.ConnectionId, username);
return username;
}
// The variable 'contextIdClient' is equal to Context.ConnectionId of the user,
// once logged in. You have to store that 'id' inside a dictionaty for example.
Clients.Client(contextIdClient).send("Hello!");
Look at SignalR Tests for the feature.
Test "SendToUser" takes automatically the user identity passed by using a regular owin authentication library.
The scenario is you have a user who has connected from multiple devices/browsers and you want to push a message to all his active connections.
Old thread, but just came across this in a sample:
services.AddSignalR()
.AddAzureSignalR(options =>
{
options.ClaimsProvider = context => new[]
{
new Claim(ClaimTypes.NameIdentifier, context.Request.Query["username"])
};
});
For anyone trying to do this in asp.net core. You can use claims.
public class CustomEmailProvider : IUserIdProvider
{
public virtual string GetUserId(HubConnectionContext connection)
{
return connection.User?.FindFirst(ClaimTypes.Email)?.Value;
}
}
Any identifier can be used, but it must be unique. If you use a name identifier for example, it means if there are multiple users with the same name as the recipient, the message would be delivered to them as well. I have chosen email because it is unique to every user.
Then register the service in the startup class.
services.AddSingleton<IUserIdProvider, CustomEmailProvider>();
Next. Add the claims during user registration.
var result = await _userManager.CreateAsync(user, Model.Password);
if (result.Succeeded)
{
await _userManager.AddClaimAsync(user, new Claim(ClaimTypes.Email, Model.Email));
}
To send message to the specific user.
public class ChatHub : Hub
{
public async Task SendMessage(string receiver, string message)
{
await Clients.User(receiver).SendAsync("ReceiveMessage", message);
}
}
Note: The message sender won't be notified the message is sent. If you want a notification on the sender's end. Change the SendMessage method to this.
public async Task SendMessage(string sender, string receiver, string message)
{
await Clients.Users(sender, receiver).SendAsync("ReceiveMessage", message);
}
These steps are only necessary if you need to change the default identifier. Otherwise, skip to the last step where you can simply send messages by passing userIds or connectionIds to SendMessage. For more

Call a WCF Data service boolean method

I'm trying to receive an answer from a WCF method from the client. When I try to execute void methods, they are working fine. For example:
Uri u = new Uri(string.Format(LogIn.ctx.BaseUri + "/CreateRole?name='{0}'",
TextBox1.Text), UriKind.RelativeOrAbsolute);
LogIn.ctx.Execute(u, "GET");
Now I want to call a method which returns a boolean, and this value will be used. Here's the method I want to call and receive its returned value:
[WebGet]
public bool Controler(string role, string user)
{
if (Roles.IsUserInRole(user, role))
{
return true;
}
else
{
return false;
}
}
Instead of LogIn.ctx.Execute(u, "GET"), try this:
IEnumerable<bool> result = LogIn.ctx.Execute<bool>(u);
bool answer = result.Single();

How to retrieve Membership's PasswordAnswer

It is encypted in the table. I don't want to reset the password.
I got a solution at here
But I am not sure the namespace of
base.DecryptPassword
Because I got an error, can not find it.
Updated again:
updated my code:
public class FalseMembershipProvider: MembershipProvider
{
public string GetPasswordAnswer(Guid providerUserKey)
{
Microsoft.Practices.EnterpriseLibrary.Data.Database db = Microsoft.Practices.EnterpriseLibrary.Data.DatabaseFactory.CreateDatabase();
using (System.Data.Common.DbCommand cmd = db.GetSqlStringCommand("SELECT PasswordAnswer FROM aspnet_Membership WHERE UserID=#UserID"))
{
db.AddInParameter(cmd, "#UserId", DbType.Guid, providerUserKey);
object answer = db.ExecuteScalar(cmd); if (answer != null)
return ProviderDecryptor(answer.ToString());
else
return null;
}
db = null;
}
internal string ProviderDecryptor(string encryptedText)
{
string decrypted = null;
if (!string.IsNullOrEmpty(encryptedText))
{
byte[] encodedbytes = Convert.FromBase64String(encryptedText);
byte[] decryptedbytes = base.DecryptPassword(encodedbytes);
if (decryptedbytes != null)
decrypted = System.Text.Encoding.Unicode.GetString(decryptedbytes, 16, decryptedbytes.Length - 16);
}
return decrypted;
}
}
The class is inheriting from the MembershipProvider class. The method being called is MembershipProvider.DecryptPassword. However, as you can see on the MSDN page, it's a protected method. By inherting from MembershipProvider, this new class can use base.DecryptPassword which is essentially saying "call the DecryptPassword method of the MembershipProvider. Even though the method is protected, I can call it because I have permission since I'm inheriting from the MembershipProvider class".
The class you're writing needs to inherit from MembershipProvider as the author did in their example:
public class FalseMembershipProvider : MembershipProvider

AuthorizationManager based on service invocation parameters

I'm currently developing my own AuthorizationManager, it looks something like that:
public class MyAuthorizationManager : ServiceAuthorizationManager
{
static bool initialize = false;
public override bool CheckAccess(OperationContext operationContext)
{
ServiceSecurityContext context = ServiceSecurityContext.Current;
string[] roles = Roles.GetRolesForUser(operationContext.ServiceSecurityContext.PrimaryIdentity.Name);
return roles.Count() > 0;
}
public override bool CheckAccess(OperationContext operationContext, ref System.ServiceModel.Channels.Message message)
{
MessageBuffer buffer = operationContext.RequestContext.RequestMessage.CreateBufferedCopy(int.MaxValue);
message = buffer.CreateMessage();
Console.WriteLine(message);
return base.CheckAccess(operationContext, ref message);
}
}
I would like to perform authorization check based on a service contract parameter, in example, if contract looks like:
[ServiceContract]
public interface IServerContract
{
[OperationContract]
[ServiceKnownType(typeof(ChildTypeOne))]
[ServiceKnownType(typeof(ChildTypeTwo))]
string SecuredMessage(ParentType incoming);
}
My goal is authorizing depending on type, in example, authorizing if incoming date is ChildTypeOne and deniying in case it was ChildTypeTwo.
I've checked "Message" and it looks like:
It must be decrypted
Seems to be highly dependent on binding
Is there any easy way to simply get parameter type?
Ok, i've figured out how to perform that. Anyway, if you know any better way to do so, let me know:
Here is the AuthorizationManager i'm using:
public class MyAuthorizationManager : ServiceAuthorizationManager
{
static bool initialize = false;
public override bool CheckAccess(OperationContext operationContext, ref System.ServiceModel.Channels.Message message)
{
bool returnedValue = base.CheckAccess(operationContext, ref message);
// messags in WCF are always read-once
// we create one copy to work with, and one copy to return back to the plumbing
MessageBuffer buffer = operationContext.RequestContext.RequestMessage.CreateBufferedCopy(int.MaxValue);
message = buffer.CreateMessage();
// get the username vale using XPath
XPathNavigator nav = buffer.CreateNavigator();
StandardNamespaceManager nsm = new StandardNamespaceManager(nav.NameTable);
nav = nav.SelectSingleNode("//#i:type",nsm);
returnedValue &= (nav.ToString() == "a:"+typeof(ChildTypeOne).Name);
return returnedValue;
}
public class StandardNamespaceManager : XmlNamespaceManager
{
public StandardNamespaceManager(XmlNameTable nameTable)
: base(nameTable)
{
this.AddNamespace("s", "http://schemas.xmlsoap.org/soap/envelope/");
this.AddNamespace("s11", "http://schemas.xmlsoap.org/soap/envelope/");
this.AddNamespace("s12", "http://www.w3.org/2003/05/soap-envelope");
this.AddNamespace("wsaAugust2004", "http://schemas.xmlsoap.org/ws/2004/08/addressing");
this.AddNamespace("wsa10", "http://www.w3.org/2005/08/addressing");
this.AddNamespace("i", "http://www.w3.org/2001/XMLSchema-instance");
}
}
}
Previous AuthorizationManager will work rejecting "ChildTypeTwo". You can use a RoleProvider in order to get role based on type.

Resources