I am working with Microsoft.AspNetCore.Identity.UserManager and I'm trying to mock the creation of a new user. In fact, it does create a new user with username, email etc. but the password hash property is still null.
This is how I set up mock usermanager with some additional setup:
var store = new Mock<IUserPasswordStore<User>>();
var validator = new UserValidator<User>();
var passValidator = new PasswordValidator<User>();
var mgr = new Mock<UserManager<User>>(store.Object, null, null, null, null, null, null, null, null);
mgr.Object.UserValidators.Add(validator);
mgr.Object.PasswordValidators.Add(passValidator);
mgr.Object.PasswordHasher = new PasswordHasher<User>();
mgr.Object.Options = AuthenticationRules.DefaultAuthenticationRules();
List<User> users= new List<User>();
mgr.Setup(x => x.DeleteAsync(It.IsAny<User>())).ReturnsAsync(IdentityResult.Success);
mgr.Setup(x => x.CreateAsync(It.IsAny<User>(), It.IsAny<string>())).ReturnsAsync(IdentityResult.Success).Callback<User, string>((x, y) => users.Add(x));
mgr.Setup(x => x.UpdateAsync(It.IsAny<User>())).ReturnsAsync(IdentityResult.Success);
The 'DefaultAuthenticationRules' returns this:
public static IdentityOptions DefaultAuthenticationRules(IdentityOptions identityOptions = null)
{
if(identityOptions == null)
identityOptions = new IdentityOptions();
identityOptions.User.RequireUniqueEmail = true;
identityOptions.Password.RequireNonAlphanumeric = false;
identityOptions.Password.RequiredUniqueChars = 0;
return identityOptions;
}
I am then passing the mgr.Object to a method that handles the creation of the new user where 'Object' is _userManager
var creationResult = await _userManager.CreateAsync(_user, _registrationModel.Password);
if (!creationResult.Succeeded)
return false;
return true;
_registrationModel.Password IS populated.
So now when the setup adding a new user in the callback to the list of users the user is populated without password hash. I am not exactly sure what I'm missing here. I'm missing something in mgr.Setup?
Thanks in advance
The callback function of yours Callback<User, string>((x, y) => users.Add(x)); will be executed after the completion of the test, it will accept as a parameter the same parameter value that you pass to the mocked method, in your case the password is probably an empty string or null.
_registrationModel.Password IS populated.
Ok, but the inner code that generate some password does not have any influence on the (x,y) => {..} params.
See this code (copied from this great answer):
public interface IFoo
{
int Bar(bool b);
}
var mock = new Mock<IFoo>();
mock.Setup(mc => mc.Bar(It.IsAny<bool>()))
.Callback<bool>(b => Console.WriteLine("Bar called with: " + b))
.Returns(99999);
var ret1 = mock.Object.Bar(true);
Console.WriteLine("Result ret1: " + ret1);
var ret2 = mock.Object.Bar(false);
Console.WriteLine("Result ret2: " + ret2);
//OUTPUT:
//Result ret1: true.
//Result ret1: false.
In the example, regardless of what is happening inside the Bar method, the output will be depend only upon the calling param value of the mocked function.
Related
I have a mocked executor that Asserts the unit after x amount of callbacks depending on what values we give to the parameters. Here's a sample code of my unit test
[Test]
[TestCase("foo", false, 2)]
[TestCase("foo", true, 3)]
public void CommandLineShouldBeValidTest(string parameter1, bool parameter2, int finalCallbackCounter)
{
int initialCallbackCounter = 0;
var executorMock = new Mock<ITaskExecutor>();
executorMock.Setup(m => m.Execute(It.IsAny<IExecutionContext>(), It.IsAny<ITask>()))
.Callback<IExecutionContext, ITask>((c, it) =>
{
var p = (ProcessTask)it;
initialCallbackCounter++;
if (initialCallbackCounter == finalCallbackCounter)
{
Assert.AreEqual(expectedCommandLine, p.CommandLine);
}
});
var macro = new Macro(parameter1, parameter2, executorMock.Object);
macro.Execute();
}
For the moment I'm using the finalCallbackCounter parameter for it, so for example if I make the second boolean parameter true instead of false, I need to change it to 3 (I actually have more arguments and cases in my current code, I just simplified it for the question's purpose).
This way to do it feels really finnicky and not very futureproof. Is there a more elegant solution to this problem?
You could capture the ITasks, then assert at the end.
Setup is not intended for assertions:
var capturedTasks = new List<ITask>();
var executorMock = new Mock<ITaskExecutor>();
executorMock.Setup(m => m.Execute(
It.IsAny<IExecutionContext>(),
Capture.In(capturedTasks)));
var macro = new Macro(mock.Object);
macro.Execute();
if (capturedTasks.Count >= finalCallbackCounter)
{
var p = (ProcessTask)capturedTasks[finalCallbackCounter - 1];
Assert.AreEqual(expectedCommandLine, p.CommandLine);
}
I'm trying to use InMemory Database to help test out my methods.
I'm having trouble running multiple DataRows in a test because all but one will give me an error when trying to add a default record to the context. If I manually run each individually they all pass but as soon as I run the whole test they fail when all but one reach mCntx.Brokers.Add(new() ... with the following error.
System.InvalidOperationException: 'The instance of entity type 'Broker' cannot be tracked because another instance with the same key value for {'Id'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the conflicting key values.'
I've tried setting up a new Db name as is recommended but it still seems to draw from the same ChangeTracker.
Test Code
[TestMethod()]
[DataRow(null, _ValidBrokerNameNotInDb, _ValidBrokerageIdInDb, _ValidBrokerageNameInDb)]
[DataRow(null, _ValidBrokerNameNotInDb, null, _ValidBrokerageNameInDb)]
[DataRow(null, _ValidBrokerNameNotInDb, _ValidBrokerageIdInDb, null)]
[DataRow(null, _ValidBrokerNameNotInDb, null, _ValidBrokerageNameNotInDb)]
public void BrokerValidationTest_CreateInDb(int? brokerId, string brokerName, int? brokerageId, string brokerageName)
{
DataTable dataTable = GetDataTable(brokerId, brokerName, brokerageId, brokerageName);
List<UploadIssue> uploadIssues = new();
var rand = new Random().NextDouble();
using (SqlDboDbContext cntx = GetContext($"BrokerValidationTest_CreateInDb{rand}"))
{
InsertUpdateDatabaseService.ImportBroker(dataTable, cntx, ref uploadIssues);
cntx.ChangeTracker.Clear();
}
Assert.IsFalse(uploadIssues.Any(x => x.UploadIssueType == UploadIssueTypes.Error));
Assert.IsFalse(uploadIssues.Any(x => x.UploadIssueType == UploadIssueTypes.DependencyError));
Assert.IsFalse(uploadIssues.Any(x => x.UploadIssueType == UploadIssueTypes.Prompt));
}
public static SqlDboDbContext GetContext(string dbName)
{
var rand = new Random().NextDouble();
var options = new DbContextOptionsBuilder<SqlDboDbContext>()
.UseInMemoryDatabase(databaseName: dbName + rand.ToString())
.Options;
SqlDboDbContext mCntx = new SqlDboDbContext(options);
mCntx.Brokers.Add(new()
{
Id = _ValidBrokerIdInDb,
Name = _ValidBrokerNameInDb,
BrokerageId = _ValidBrokerageIdInDb,
Brokerage = DbBrokerage
});
mCntx.SaveChanges();
return mCntx;
}
The issue it seems to be with Brokerage = DbBrokerage
private static Brokerage DbBrokerage = new()
{
Id = _ValidBrokerageIdInDb,
Name = _ValidBrokerageNameInDb
};
Once I changed it to creating a new instance of Brokerage instead of using the static instance it started working.
From
mCntx.Brokers.Add(new()
{
Id = _ValidBrokerIdInDb,
Name = _ValidBrokerNameInDb,
BrokerageId = _ValidBrokerageIdInDb,
Brokerage = DbBrokerage
});
To
mCntx.Brokers.Add(new()
{
Id = _ValidBrokerIdInDb,
Name = _ValidBrokerNameInDb,
BrokerageId = _ValidBrokerageIdInDb,
Brokerage = new()
{
Id = _ValidBrokerageIdInDb,
Name = _ValidBrokerageNameInDb
}
});
I am new to MoQ framework. I am writing unit testing for controller using MoQ framework and here is my test method,
var mockedItemDetail = new ItemDetail()
{
Name = null
};
var mockObject = new Mock<IItem>();
mockObject.Setup(x => x.GetItemDetail()).Returns(mockedItemDetail);
var result = myController.GetDetails() as ViewResult;
Here is my Controller Method,
public ActionResult GetDetails()
{
var controllerItemDetail = new ItemDetail();
controllerItemDetail = _item.GetItemDetail();
controllerItemDetail.Name = "Changed Name";
return View("ViewName", controllerItemDetail);
}
Test runs and now I want to assert the sent mockedItemDetail and received model result controllerItemDetail.
In above case, mockedItemDetail property "Name" has null and received controllerItemDetail property Name as "Changed Name".
But whenever I debug, after calling the test method GetDetails(),
My mockedItemDetail property Name is also updated as "Changed Name" in the current scope and I don't know why? Is this is the actual behavior of MoQ?
Edited Content
Consider the same above case in below mock list, here the change in the mock object will not update in all contexts. i.e. The list count for mockedItemDetailList remains 0 and the list count of controllerItemDetail is 1 even after test method calls. Why?
Test method:
var mockedItemDetailList = new List<ItemDetail>();
var mockObject = new Mock<IItem>();
mockObject.Setup(x => x.GetListOfItemDetail()).Returns(mockedItemDetailList);
var result = myController.GetDetails() as ViewResult;
Controller method:
public ActionResult GetDetails()
{
var controllerItemDetail = new ItemDetail();
controllerItemDetail = _item.GetListOfItemDetail();
controllerItemDetail.Add(new ItemDetail(){
Name = "Changed Name"
});
return View("ViewName", controllerItemDetail);
}
You have a very specific object:
var mockedItemDetail = new ItemDetail()
{
Name = null
};
When you call mockObject.Setup(x => x.GetItemDetail()).Returns(mockedItemDetail);, you're returning the reference to mockItemDetail. Any changes on that object will update in all contexts.
A quick follow up. To have it return a blank new ItemDetail() each time, you can simply use the lambda method of Returns():
mockObject.Setup(x => x.GetItemDetail()).Returns(() => new ItemDetail()
{
Name = null
});
This may not be something that's even possible but I thought I'd ask anyway. Is there anyway for me to stub out this method so that the second call is also stubbed out using the parameter provided in the method I'm testing?
The method to stub:
public SupportDetails GetSupportDetails(string languageKey)
{
var result = FindSupportDetails(languageKey);
return result ?? FindSupportDetails("en-us");
}
My Current test:
public void GetsUSDetails_IfLangKeyDoesNotExist()
{
var langKey = "it-it";
_repo.Stub(s => s.FindSupportDetails(langKey))
.Return(supportDetails.Where(sd => sd.LanguageKey == langKey)
.SingleOrDefault());
ISupportRepository repo = _repo;
var actual = repo.GetSupportDetails(langKey);
Assert.AreEqual("en-us", actual.LanguageKey);
}
and the supportDetails object used in the test:
supportDetails = new SupportDetails[]
{
new SupportDetails()
{
ContactSupportDetailsID = 1,
LanguageKey = "en-us"
},
new SupportDetails()
{
ContactSupportDetailsID = 2,
LanguageKey = "en-gb"
},
new SupportDetails()
{
ContactSupportDetailsID = 3,
LanguageKey = "es-es"
}
};
The correct and the most elegant solution to your problem is to use Do method:
_repo.Stub(s => s.FindSupportDetails(null))
.IgnoreArguments()
.Do((Func<string, SupportDetails>)
(langKey => supportDetails.SingleOrDefault(sd => sd.LanguageKey == langKey)));
The Func will raise no matter what argument was passed to FindSupportDetails, then the correct SupportDetails will select.
I'm using this code below to try to update the values in a dictionary object depending on its key.
public static ConcurrentDictionary<string, SingleUserStatisticsViewModel> UsersViewModel = new ConcurrentDictionary<string, SingleUserStatisticsViewModel>();
var userSession = new UserSessionStatistic()
{
Id = "12345", Browser = "Netscape"
};
var userViewModel = new SingleUserStatisticsViewModel()
{
UserSessionStatistic = userSession,
StartTime = DateTime.Now
};
//Add first time
MyStaticClass.UsersViewModel.AddOrUpdate(userViewModel.UserSessionStatistic.Id, userViewModel, (key, model) => model);
//try to Update
var userSession2 = new UserSessionStatistic()
{
Id = "12345",
Browser = "not getting updated????"
};
var userViewModel2 = new SingleUserStatisticsViewModel()
{
UserSessionStatistic = userSession2,
StartTime = DateTime.Now
};
MyStaticClass.UsersViewModel.AddOrUpdate(userViewModel2.UserSessionStatistic.Id, userViewModel2, (key, model) => model);
But the UsersessionStatistic object in userViewModel2 is not getting updated in the ConcurrentDictionary (it's Browser propery still says "Netscape"), what am I doing wrong?
About the value factory, the docs say:
updateValueFactory Type: System.Func The
function used to generate a new value for an existing key based on the
key's existing value
Which means your passing it the existing value. You need to update it with the new one instead:
MyStaticClass.UsersViewModel.AddOrUpdate(userViewModel2.UserSessionStatistic.Id,
userViewModel2,
(key, oldModel) => userViewModel2);