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.
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 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 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 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
});
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