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
});
Related
i have a test where i will be comparing two objects.
i am open to know whats the best way to do it.
i have created something for which i have an issue that needs help.
following code has an object property that needs to be present
i would like to assert that all fields to be present except the id property.
i feel like the last 5 statements feel inappropriate, if there is a clearer way of doing it, i would like to know.
[Fact]
public void CreateTransaction_AddFirstTransaction_ShouldUpdateTransactionJson()
{
// Arrange
var mockFileSystem = new MockFileSystem();
var buyCrypto = new BuyCrypto(mockFileSystem);
var bitcoin = new Currency()
{
name = "bitcoin",
code = "btc",
price = 10
};
// Act
buyCrypto.CreateTransaction(true, bitcoin, 10);
//Assert
var result = JsonSerializer
.Deserialize<List<Transaction>>(mockFileSystem.GetFile(TransactionJson).TextContents);
Assert.Equal("bitcoin", result[0].currency);
Assert.Equal(DateTime.Now.ToString(), result[0].dateTime);
Assert.Equal("TestName", result[0].name);
Assert.Equal(10, result[0].quantity);
Assert.Equal(100, result[0].total);
}
I love using Fluent Assertions for these kinds of tests (docs). You could do something like this:
// Arrange
// ... other stuff
var expectedTransaction = new Transaction {
currency = "bitcoin",
dateTime = DateTime.Now.ToString(),
name = "TestName",
quantity = 10,
total = 100 };
// Act
// ...
// Assert
result[0].Should().BeEquivalentTo(expectedTransaction, options => options.Excluding(t => t.Id));
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.
I know that this code is not good and maybe it's stupid question but can anybody explain me why it works in this way?
I have this simple class
public class Customer
{
public string Name { get; set; }
}
And I have next method with Action delegate
private Customer GetCustomer(Action<Customer> action)
{
var model = new Customer { Name = "Name 1" };
action?.Invoke(model);
return model;
}
Then I execute this method with delegate and write into console model.Name
var model = GetCustomer(c => c = new Customer { Name = "Name 2" });
Console.WriteLine(model.Name);
I expected to get "Name 2". But I get "Name 1" - which is value of the class defined inside method. I understand that to get what I want - I could write code in this way:
var model = GetCustomer(c => c.Name = "Name 2");
Console.WriteLine(model.Name);
And everything will be ok. But why my first implementation doesn't work?
I would be thankful a lot if somebody explain me this.
c in your lambda is only assigning to the parameter, its not assinging it back to model in GetCustomer.
Lambda expression in your case compiles into a method.
This is the same, why this code won't change c.Name:
// code below is equivalent of
// c => c = new Customer { Name = "Name 2" }
private void Foo(Customer c)
{
// c will be changed inside method,
// but will remain unchanged outside it
c = new Customer { Name = "Name 2" };
}
In your lambda expression, you are just saying that c and model are pointing to the same reference. And then you are changing the reference of c. At the end model is still referencing to the previous instance. This is the equivalent code of what you are trying to do:
var model = new Customer { Name = "Name 1" };
var c = model;
c = new Customer { Name = "Name 2" };
But, model.Name is still Name1.
You can change your code as if you want to change one of hte properties:
var model = GetCustomer(c => c.Name = "Name2");
Console.WriteLine(model.Name);
var model = GetCustomer(c => c.Name = "Name 2");
instead of:
var model = GetCustomer(c => c = new Customer { Name = "Name 2" });
Why? Because you want to do the changes on the same instance transferred in the new function (implements the delegate). If you create a new instance of Customer in new function (c) then it would do the changes on the new instance of Customer that won't be valid when c ends.
Same here but with List.
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 am trying to get information from a database, convert it to a list and return the CustomerList. This first bit of code works fine. The second example is where I’m trying to accomplish the same thing except the fields are coming from my database. What’s wrong with what I’m doing and how can I make this work? The second piece of code works elsewhere in my project but not here.
private SchoolIn.Models.CustomerList CreateCustomerList()
{
return new SchoolIn.Models.CustomerList()
{
new SchoolIn.Models.Customer { Id = 1, Name = "Patrick", Address = "Geuzenstraat 29", Place = "Amsterdam" },
new SchoolIn.Models.Customer{ Id = 2, Name = "Fred", Address = "Flink 9a", Place = "Rotterdam" },
new SchoolIn.Models.Customer { Id = 3, Name = "Sjonnie", Address = "Paternatenplaats 44", Place = "Enkhuizen" },
new SchoolIn.Models.Customer { Id = 4, Name = "Henk", Address = "Wakerdijk 74", Place = "Utrecht" },
new SchoolIn.Models.Customer { Id = 5, Name = "Klaas", Address = "Paternatenplaats 44", Place = "Plaantan" }
};
}
private SchoolIn.Models.CustomerList CreateCustomerList()
{
return new SchoolIn.Models.CustomerList()
{
SchoolInDB db = new SchoolIn.Models.SchoolInDB();
var courseprogresses = db.CourseProgresses.Include(c => c.Course).Include(c => c.Teacher);
return View(courseprogresses.ToList());
};
}
First things first the second code is invalid C#. So I suppose it doesn't event compile. You cannot use such expressions in an object initialization syntax. Please learn C# before getting into ASP.NET MVC.
The other problem is that your method is private and you are attempting to return View which is something that you do in a controller action. The view method returns an ActionResult whereas your method return type is SchoolIn.Models.CustomerList which once again is wrong.
So move this into some controller action where you would instantiate your database access context and then perform the query and return the model to the corresponding view for display:
public class HomeController: Controller
{
...
public ActionResult CreateCustomerList()
{
SchoolInDB db = new SchoolIn.Models.SchoolInDB();
var courseprogresses = db
.CourseProgresses
.Include(c => c.Course)
.Include(c => c.Teacher)
.ToList();
return View(courseprogresses);
}
}
and if you wanted to keep this into a separate method:
private List<SchoolIn.Models.CourseProgress> CreateCustomerList()
{
SchoolInDB db = new SchoolIn.Models.SchoolInDB();
return db
.CourseProgresses
.Include(c => c.Course)
.Include(c => c.Teacher)
.ToList();
}
In your first function you are returning actual list while in second function you are not returning list, instead you have returned the View with model as a list. So for it you need to return View Result viz. ActionResult.
Hope it helps