How can I setup a multi-item list with moq - c#

I'm trying to write a unit test for my method but failed. I want to return the list with the 2 item response of GetMyRequest with Mock(package Moq), then I run Task.WhenAll with the response returned with my method. So I used the SetupSequence method but it returned a single-item list. How can I return a multi-item list in my test.
public void MyMethod()
{
ProductService = new Mock<IProductService>();
var myResponse = myStringList.Select(async x => await _myService.GetMyRequest(x, null, null)).ToList();
//my response type -> List<Task<ResponseModel>>
var myResponses = await Task.WhenAll(myResponse);
}
I want myResponses multi-item list with mocking but it single-item.
Unit tests
public async Task InitializeAsync(){
ServiceResponse = Fixture.Build<MyModel>().CreateMany(2).ToList();
MyService = new Mock<IProductService>();
MyService.SetupSequence(service => service.GetMyRequest(It.IsAny<string>(), null, null))
.ReturnsAsync(ServiceResponse[0])
.ReturnsAsync(ServiceResponse[1]);
}

TL; DR: You can't return multiple items for your GetMyRequest
SetupSequence means that you want to setup your GetMyRequest in a way that multiple calls against it will result with different responses.
For the first call you will receive the value of ServiceResponse[0]
For the second call you will receive the value of ServiceResponse[1]
According to my understanding the return type of GetMyRequest is Task<ServiceResponse> , that's why you can't define a mock to return Task<IEnumerable<ServiceResponse>> or something similar.

Related

Mock: SetReturnsDefault for async methods always return null

I want to mock a service to always return a default object.
I tried this but it still always return null:
var service = new Mock<MyService>();
service.SetReturnsDefault(Task.FromResult(new ServiceResult<object>
{
Status = ResultStatus.OK,
Value = null
}));
The methods are async and return Task<IServiceResult<object>> where object can be any object (also a collection of objects).
ServiceResult implement the IServiceResult interface.
Why is it not working? I would like to avoid setup every method...
Thanks for your help!
EDIT:
I use for example a method like this but it result is always null instead of the default ServiceResult:
var result = await _service.GetEntityAssetUsagesAsync(EntityTypeEnum.Radio, radio.MediaChannelId);
Here is (a piece of) my interface:
Task<IServiceResult<List<EntityAssetUsageDto>>> GetEntityAssetUsagesAsync(EntityTypeEnum entityType, int entityId);
Task<IServiceResult<List<AssetDto>>> GetAssetsAsync(List<long> assetIds, bool withoutException = false);

NSubstitute returning empty string

I have the following method under test:
public HomeController(IUserIpAddressHelper userIpAddressHelper)
{
_userIpAddressHelper = userIpAddressHelper;
}
[HttpGet]
public ActionResult Index()
{
var userIpAddress = _userIpAddressHelper.GetUserIpAddress(System.Web.HttpContext.Current);
if (_userIpAddressHelper.IsIpAddressOddOrEven(userIpAddress))
{
return RedirectToAction(HomePage);
}
return RedirectToAction(HomePageAlternative);
}
and I am testing as follows:
public void Test()
{
var userIpAddressHelper = Substitute.For<IUserIpAddressHelper>();
userIpAddressHelper.GetUserIpAddress(Arg.Any<HttpContext>()).Returns("0.0.0.2");
var controller = new HomeController(userIpAddressHelper);
var result = controller.Index();
Assert.IsInstanceOf<RedirectToRouteResult>(result);
var redirectToRouteResult = result as RedirectToRouteResult;
Assert.AreEqual(HomeController.HomePage, redirectToRouteResult.RouteValues["action"]);
}
However the test is failing due to the value of userIpAddress being "" an empty string, instead of 0.0.0.2 as I've set it. Can anyone please point out where I've gone wrong here?
Is userIpAddress definitely ""? It looks like the Returns in your original test is specified well, but if IUserIpAddressHelper is an interface then the substitute for it will not have a result stubbed for IsIpAddressOddOrEven, so it will always return false even if GetUserIpAddress is stubbed to return "0.0.0.2".
To get the test to mirror how the production code passes through the data, you can stub out both members:
var userIpAddressHelper = Substitute.For<IUserIpAddressHelper>();
userIpAddressHelper.GetUserIpAddress(Arg.Any<HttpContext>()).Returns("0.0.0.2");
userIpAddressHelper.IsIpAddressOddOrEven("0.0.0.2").Returns(true);
This will test that the production code correctly passes through the result of GetUserIpAddress to IsIpAddressOddOrEven.
Note: we could also stub these to work with "ip-address-result" and it would still work. We don't need a valid odd/even result returned, as we are not using a real implementation of IUserIpAddressHelper, just a substitute for testing. If you find it necessary to substitute for IUserIpAddressHelper in lots of tests and you want it to act like a real implementation (i.e. it will actually return whether an address is odd or even), it might be easier to write a TestUserIpAddressHelper.
Another way to avoid having the dependency between the results of GetUserIpAddress and IsIpAddressOddOrEven is to change the interface to have a bool IsIpAddressOddOrEven(HttpContext context) method that combines both operations. That way you would only need to stub one for the test.
If you have problems with System.Web.HttpContext.Current, you can try to mock IsIpAddressOddOrEven method instead. They will both do the same job for your test.
Like this:
public void Test()
{
var userIpAddressHelper = Substitute.For<IUserIpAddressHelper>();
userIpAddressHelper.IsIpAddressOddOrEven(Arg.Any<string>()).Returns(true);
var controller = new HomeController(userIpAddressHelper);
var result = controller.Index();
Assert.IsInstanceOf<RedirectToRouteResult>(result);
var redirectToRouteResult = result as RedirectToRouteResult;
Assert.AreEqual(HomeController.HomePage, redirectToRouteResult.RouteValues["action"]);
}

Mocking while loop

I need to mock a while loop to run just once however my setup makes it run infinite times as I think it returns true always.
My Set up:
var loginName = "12345";
cacheRepository.Setup(m => m.AddString(string.Format("{0}_{1}", Resources.ResetCodeCacheKey, randomCode), loginName)).Returns(true);
While loop in method:
while (_cacheRepository.AddString(string.Format("{0}_{1}", Resources.ResetCodeCacheKey, code), userLoginName))
{
//.........
}
Add String implementation:
public virtual bool AddString(string key, string value)
{
if (!ExistsKey(key))
{
Cache.AddString(key, value);
return true;
}
return false;
}
How can I set up my method to return true just once? A code snippet will be helpful.Thanks for looking into this.
Use SetupSequence to setup the mocked member to return the desired results.
For example say you have the following interface
public interface IInterface {
bool AddString(string key, string value);
}
The setup would look like
var cacheRepository = new Mock<IInterface>();
cacheRepository
.SetupSequence(m => m.AddString(It.IsAny<string>(), It.IsAny<string>()))
.Returns(true)
.Returns(false);
When the mocked member is called the first time is will return true and then false the second time.
Reference Moq Quickstart to get a better understanding of how to use the mocking framework.
Setting up a member to return different values / throw exceptions on sequential calls:
var mock = new Mock<IFoo>();
mock.SetupSequence(f => f.GetCount())
.Returns(3) // will be returned on 1st invocation
.Returns(2) // will be returned on 2nd invocation
.Returns(1) // will be returned on 3rd invocation
.Returns(0) // will be returned on 4th invocation
.Throws(new InvalidOperationException()); // will be thrown on 5th invocation

Mock HttpResponseMessage while unit testing API with Moq and AutoFixture

I am writing unit tests for the existing Web API 2 project. For which i am using Ploeh Autofixture and Moq.
Test Method :
UPDATED
[Test]
public async Task Service1_TestMethod() {
//some code here
var fakeemail = FakeBuilder.Create<string>("test1234#test.com");
var fakeUserInvite =
FakeBuilder.Build<UserInvite>()
.With(i => i.EmailAddress, fakeemail)
.With(i => i.Username, fakeemail)
.Create();
var fakeUserToken = FakeBuilder.Create<string>();
var fakeHttpResponseMessage =
Fixture.Build<HttpResponseMessage>()
.With(h => h.StatusCode, HttpStatusCode.OK).Create();
//Here i am mocking another service method. Whose response is HttpResponseMessage.
Service2.Setup(i => i.AddUser(fakeUserInvite, fakeUserToken))
.ReturnsAsync(fakeHttpResponseMessage);
var result = await Service1.AddUser( /* */);
}
Service1 Method :
public async Task<bool> AddUser(/* */)
{
var response = await Service2.AddUser(userInvite, userToken); // response is null even after mocking it.
// Add user code follows bassed on the above response.
}
If i comment the Service2.AddUser call then everything works. There is a lot of code in that method apart from this call. I am having problem with only this call. If this call returns the mocked HttpResponseMessage then everything works.
Service2 is an external API. I am just wondering how to mock HttpResponseMessage. Any help is appreciated.
The stub you create with:
service2.Setup(i => i.AddUser(fakeUserInvite, fakeUserToken))
.ReturnsAsync(fakeHttpResponseMessage);
requires the actual call to be made with the exact same objects as the ones referenced by fakeUserInvite and fakeUserToken in order for Moq to return fakeHttpResponseMessage.
This is because Moq's argument matching verifies that the arguments specified in the stub are equal to the ones made in the actual call. If they are not, the stub won't match and Moq will return the default value for the method's return type – in this case null since HttpResponseMessage is a reference type.
To solve this problem, you can either make sure that the fakeUserInvite and fakeUserToken references are being passed to the actual service2.AddUser call or you can use somewhat less specific argument constraints.
Here's an example:
service2.Setup(i => i.AddUser(
It.Is<UserInvite>(u => u.EmailAddress == fakeEmail &&
u.Username == fakeEmail),
fakeUserToken))
.ReturnsAsync(fakeHttpResponseMessage);
Here we're stating that the AddUser method should be called with:
A UserInvite object whose EmailAddress and Username properties have the same value as fakeEmail as the first argument
The same value as fakeUserToken as the second argument
If the actual values of those arguments don't matter to your specific test scenario, you can tell Moq to always return fakeHttpResponseMessage regardless of what arguments AddUser is being called with by saying:
service2.Setup(i => i.AddUser(
It.IsAny<UserInvite>(),
It.IsAny<string>()))
.ReturnsAsync(fakeHttpResponseMessage);

Moq setting method return value

I have the below class, and I am trying to test the method AddRecordToQueue.
I am using Moq to mock the result of the the AddToQueue method within the AddRecordToQueue method.
The AddToQueue method returns a boolean, so i am trying to mock the result with a true value
public class Test
{
private readonly IRabbitMqConnection rabbitMqConnection;
public Test(IRabbitMqConnection rabbitMqConnection)
{
this.rabbitMqConnection = rabbitMqConnection;
}
public bool AddRecordToQueue(string messageExchange, object data)
{
var jsonified = JsonConvert.SerializeObject(data);
var customerBuffer = Encoding.UTF8.GetBytes(jsonified);
var result = this.rabbitMqConnection.AddToQueue(customerBuffer, messageExchange);
return result;
}
}
My test class looks like the below.
[TestClass]
public class TestCon
{
[TestMethod]
public void MockTest()
{
Moq.Mock<IRabbitMqConnection> rabbitConection = new Moq.Mock<IRabbitMqConnection>();
var draftContactsManager = new Test(rabbitConection.Object);
rabbitConection.Setup(e => e.AddToQueue(null, string.Empty)).Returns((bool res) => true);
var result = draftContactsManager.AddRecordToQueue("someExchange", null);
Assert.IsTrue(result);
}
}
I cant seem to set the moq result as true. Can anyone advise what I am missing
thanks
I think that you need to change the Returns to just return true instead of the lambda. Like this:
rabbitConection.Setup(e => e.AddToQueue(null, string.Empty)).Returns(true)
EDIT:
If this still doesn't work then it is probably due to the parameters not matching. You are passing in "someExchange" but the mock is set up for string.Empty. If you aren't sure what values will be used you could use the It.IsAny method to get around this.
rabbitConection.Setup(e => e.AddToQueue(It.IsAny<byte[]>(), It.IsAny<string>())).Returns(true)
You need to setup the method with the actual arguments it's invoked.
If JsonConvert.SerializeObject(data) returns null, then this is the setup:
rabbitConection.Setup(e => e.AddToQueue(null, "someExchange")).Returns(true)
Additionally, you can setup the method to return true/false regardless of values of the arguments:
rabbitConection.Setup(e => e.AddToQueue(It.IsAny<byte[]>(), It.IsAny<string>())).Returns(true)
With the above setup, the method will return true no matter what what you've passed to the method. The previous example will return true only when the method is called with the setuped arguments.
As the others said, the Setup is incorrect.
You need to call Setup before using the associated Object
It should be something similar to:
...
rabbitConection
.Setup(e => e.AddToQueue(It.IsAny<byte[]>(), It.IsAny<string>()))
.Returns(true);
var draftContactsManager = new Test(rabbitConection.Object);
...

Categories