Moq difference between Verify() and Setup()...VerifyAll() - c#

I was creating a couple of Unit Tests where I wanted to verify if a method is called with a parameter whose properties I was expecting.
So given this very simple system:
public class Employee
{
public bool IsEmployed { get; set; }
}
public class DataStore
{
public void UpdateEmployee(Employee obj)
{
// Save in DB
}
}
public interface IDataStore
{
void UpdateEmployee(Employee employee);
}
public Employee FireEmployee(IDataStore dataStore, Employee employee)
{
employee.IsEmployed = false;
dataStore.UpdateEmployee(employee);
return employee;
}
I want to verify that the DataStore.UpdateEmployee() method is called when the Employee.IsEmployed property is set to false. So here are two test cases that I believe should accomplish the same thing.
[Test]
public void TestViaVerify()
{
//Arrange
Mock<IDataStore> dataStore = new Mock<IDataStore>();
var robert = new Employee { IsEmployed = true };
//Act
FireEmployee(dataStore.Object, robert);
//Assert
dataStore.Verify(x => x.UpdateEmployee(It.Is<Employee>(e => e.IsEmployed == false)), Times.Once);
}
[Test]
public void TestViaSetupVerifyAll()
{
//Arrange
Mock<IDataStore> dataStore = new Mock<IDataStore>();
dataStore.Setup(x => x.UpdateEmployee(It.Is<Employee>(e => e.IsEmployed == false)));
var robert = new Employee { IsEmployed = true };
//Act
FireEmployee(dataStore.Object, robert);
//Assert
dataStore.VerifyAll();
}
Given the current code of the system, both test passes as expected.
Now say another developer comes along and accidentally moved the setting of the Employee.IsEmployed = false; after the DataStore.UpdateEmployee() method. Now in that case, I want my tests to fail because the employee will not be marked as unemployed in the DB.
public Employee FireEmployee(IDataStore dataStore, Employee employee)
{
dataStore.UpdateEmployee(employee);
employee.IsEmployed = false;
return employee;
}
Now when I run the test:
TestViaVerify Passes
TestViaSetupVerifyAll Fails
I was expecting both of them to fail but it looks like for the TestViaVerify() method, the lambda in the method is executed at the end of the test wherein the Employee.IsEmployed is already set to false.
Is there a way to accomplish what I want with just using the Verify method? And not have to do Setup...VerifyAll? If there is none I'll just go with TestViaVerifyAll() approach.

To be honest it is been more then 2 years since the last time I was updated with moq source code. Both Verify and VerifyAll are based on that every invocation of the fake instance is being captured(include the parameters).
Verify will looking for method/property invocation and verifies the captured invocations(with their captured parameters) while, VerifyAll will take all the setup methods and do the same as Verify method.
Since the captured parameter is ByRef parameter and if the last paragraph is still relevant you can cause your UTs failed simply by add robert.IsEmployed = true; before invoke Verify/VerifyAll:
[Test]
public void TestViaVerify()
{
....
robert.IsEmployed = true; // will make this UT to failed
//Assert
dataStore.Verify(x => x.UpdateEmployee(It.Is<Employee>(e => e.IsEmployed == false)), Times.Once);
}
[Test]
public void TestViaSetupVerifyAll()
{
....
robert.IsEmployed = true; // will make this UT to failed
//Assert
dataStore.VerifyAll();
}
I think that in the past I answered something similar(with more examples), the way I was workaround this problem is a combination between Setup and Callback since I don't like to use VerifyAll pattern:
....
var invokedCorrectly = false;
dataStore.Setup(x => x.UpdateEmployee(It.Is<Employee>(e => e.IsEmployed == false)))
.Callback<Employee>(x=> invokedCorrectly = true);
//Act
FireEmployee(dataStore.Object, robert);
//Assert
Assert.IsTrue(invokedCorrectly);

This is expected behavior in moq as arguments captured by invocation are compared by identity, using Equals not by value. Once you changed captured arguments you actually directly change the invocation. Then later on when you verify these objects are not the same anymore. As #Old Fox alredy provided one solution I will just add one more. You could use Verify() instead of VerifyAll() the difference is that the first one will only check setups marked as Verifiable(). In your case something like this:
[Test]
public void TestViaSetupVerifyAll()
{
//Arrange
Mock<IDataStore> dataStore = new Mock<IDataStore>();
dataStore
.Setup(x => x.UpdateEmployee(It.Is<Employee>(e => e.IsEmployed == false)))
.Verifiable();
var robert = new Employee { IsEmployed = true };
//Act
FireEmployee(dataStore.Object, robert);
//Assert
dataStore.Verify();
}
If you mark setup as Verifiable() you could be able to capture the particular invocation with the state of the object you actually expect.

Related

Is it possible to pass number of times invocation is met as parameter to a unit test class method?

I have a unit test class method that currently takes 1 parameter but I want to extend it to receive 2 parameters with the latter being the number of times invocation is met on a mock object.
What I currently have is something like this, which doesn't compile successfully due to errors
[Theory]
[InlineData("", Times.Never)]
[InlineData("test", Times.Once)]
public async void PostAsync_SendAsync_VerifyOnce(string id, Times outcome)
{
var mockClients = new Mock<IHubClients>();
...
...
...
mockClients.Verify(clients => clients.Client(id), outcome);
}
Is it possible to achieve something like this? So in theory both tests here should pass, the first will never be invocated as the first parameter is blank and the second test will be invocated once as the parameter is valid.
You can achieve this using the Times.Exactly method:
[Theory]
[InlineData("", 0)]
[InlineData("test", 1)]
public async void PostAsync_SendAsync_VerifyOnce(string id, int expectedCalls)
{
var mockClients = new Mock<IHubClients>();
...
...
...
mockClients.Verify(clients => clients.Client(id), Times.Exactly(expectedCalls));
}
Use a TheoryData and bind it to your test via a MemberDataAttribute, that allows to make those Times.Once() and Times.Never() method calls.
It also allows other scenarios like AtLeast and AtMost.
public static TheoryData GetTestCases()
{
return new TheoryData<string, Times>()
{
{ "", Times.Never() },
{ "test", Times.Once() }
};
}
[Theory]
[MemberData(nameof(GetTestCases))]
public void PostAsyncSendAsyncVerifyOnce(string id, Times outcome)
{
var mockClients = new Mock<IHubClients>();
// ...
mockClients.Verify(clients => clients.Client(id), outcome);
}

How would I mock a UserPrincipal?

I'm trying to actually test my code for once, but mocking has always been (one of) my Achilles Heel(s).
I'm doing some stuff with AD, and want to test without actually touch it. Here's what I'm doing:
var userPrincipalMock = new Mock<UserPrincipal>();
userPrincipalMock.Setup(x => x.SamAccountName).Returns("somesamaccountname");
userPrincipalMock.Setup(x => x.EmployeeId).Returns("anemployeenumber");
But apparently UserPrincipal doesn't wanna give up control of a samaccount name that easily:
Message "Unsupported expression: x => x.SamAccountName\nNon-overridable members (here: Principal.get_SamAccountName) may not be used in setup / verification expressions."
Any of you lovely guys or gals know the right way to go about this?
It is impossible to test methods that aren't marked as virtual. For this reason only interfaces should be mocked. To solve your problem, you can create a wrapper:
interface IUserPrincipalWrapper
{
string SamAccountName { get; set; }
string EmployeeId { get; set; }
void Delete();
... // Add all methods and properties of UserPrincipal that you need
}
class UserPrincipalWrapper : IUserPrincipalWrapper
{
private UserPrincipal Implementation { get; }
UserPrincipalWrapper(UserPrincipal implementation)
{
Implementation = implementation;
}
public string SamAccountName
{
get => Implementation.SamAccountName;
set => Implementation.SamAccountName = value;
}
public string EmployeeId
{
get => Implementation.EmployeeId;
set => Implementation.EmployeeId = value;
}
public void Delete()
{
Implementation.Delete();
}
...
// Call or set everything to Implementation...
// This is needed to create an interface around UserPrincipal, which you can mock
}
// Factory is needed for mocking `new` calls..
// as often times, you don't want to test that either
interface IUserPrincipalFactory
{
IUserPrincipalWrapper Create(PrincipalContext context);
}
class UserPrincipalFactory : IUserPrincipalFactory
{
public IUserPrincipalWrapper Create(PrincipalContext context)
{
var implementation = new UserPrincipal(context);
return new UserPrincipalWrapper(implementation);
}
}
Then in your tests you can mock everything:
// This is your mock
var userPrincipalMock = new Mock<IUserPrincipalWrapper>();
// You need factory for mocking `new UserPrincipal();` calls
var factoryMock = new Mock<IUserPrincipalWrapperFactory>();
factoryMock.Setup(factory => factory.Create(It.IsAny<PrincipalContext>())).Returns(userPrincipalMock.Object);
// Now it works :)
userPrincipalMock.Setup(x => x.SamAccountName).Returns("somesamaccountname");
userPrincipalMock.Setup(x => x.EmployeeId).Returns("anemployeenumber");

How to setup Moq for the same method where return value depends on input?

I'd like to test a method called multiple times during test, in a way where the tested method output depends on its input. I checked Moq's documentation and the following example seems to what I need.
// matching Func<int>, lazy evaluated
mock.Setup(foo => foo.Add(It.Is<int>(i => i % 2 == 0))).Returns(true);
The tested method looks like this:
Task<DimensionStructure> GetDimensionStructureByIdAsync(DimensionStructure dimensionStructure);
The DimensionStructure class looks like this, it is simplified as on the Id is important.
public class DimensionStructure
{
public long Id { get; set; }
}
But when I put together the mock I need, example below, when it is called it returns always null.
_masterDataWebApiClientMock.Setup(m => m.GetDimensionStructureByIdAsync(
It.Is<DimensionStructure>(
structure => structure.Id == 101)
))
.ReturnsAsync(addedToRoot);
_masterDataWebApiClientMock.Setup(m => m.GetDimensionStructureByIdAsync(
It.Is<DimensionStructure>(
structure => structure.Id == 201 )
))
.ReturnsAsync(addedToFirstLevel);
The point os that the method gets different parameter and returns different objects.
In both cases, it returns null. According to the doc, it should return what is in the ReturnAsync which is not null.
It works perfectly in another test where the Id property value of the object doesn't matter.
_masterDataWebApiClientMock.Setup(m => m.GetDimensionStructureByIdAsync(It.IsAny<DimensionStructure>()))
.ReturnsAsync(addedDimensionStructure);
The question is, how to solve this?
Update - 02/29/2020
When I do not specify the call parameter then it is just working fine. It makes a bit tricky to test the method, but solvable.
_masterDataWebApiClientMock
.Setup(m => m.GetDimensionStructureByIdAsync(
It.IsAny<DimensionStructure>()
))
.ReturnsAsync(addedToRoot);
On the other hand I reported this case a special, possible bug case to Moq folks.
Having put together a test bench as follows I can see it returns correct values.
It seems either my assumptions about IClassUnderTest are wrong or you might need to ensure addedToRoot and addedToFirstLevel are defined when you call your mocked method.
This probably doesn't answer your question per se, I'm however hoping this will either point you in the right direction or will prompt for more information.
using System;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq; //I'm running Moq 4.13
namespace UnitTestProject1
{
public class DimensionStructure
{
public long Id { get; set; }
}
public interface ITest
{
Task<DimensionStructure> GetDimensionStructureByIdAsync(DimensionStructure dimensionStructure);
}
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestMethod1()
{
var addedToRoot = new DimensionStructure { Id = 1 };
var addedToFirstLevel = new DimensionStructure { Id = 2 };
var _masterDataWebApiClientMock = new Mock<ITest>();
_masterDataWebApiClientMock.Setup(m => m.GetDimensionStructureByIdAsync(
It.Is<DimensionStructure>(
structure => structure.Id == 101)
))
.ReturnsAsync(addedToRoot);
_masterDataWebApiClientMock.Setup(m => m.GetDimensionStructureByIdAsync(
It.Is<DimensionStructure>(
structure => structure.Id == 201)
))
.ReturnsAsync(addedToFirstLevel);
Assert.AreEqual(1, _masterDataWebApiClientMock.Object.GetDimensionStructureByIdAsync(new DimensionStructure { Id = 101 }).Result.Id);
Assert.AreEqual(2, _masterDataWebApiClientMock.Object.GetDimensionStructureByIdAsync(new DimensionStructure { Id = 201 }).Result.Id);
}
}
}

Setup method with FindOptions<T> as parameter in UnitTest using Moq

Imagine I have this class:
public class MyClass
{
public string Id { get; set; }
public string Name { get; set; }
public IList<MyOtherClass> MyOtherClassList { get; set; }
}
And this method on my service layer which I want to unit-test with Moq and xunit:
public IList<MyClass> GetAll()
{
var options = new FindOptions<MyClass>
{
Projection = Builders<MyClass>.Projection
.Exclude(m => m.MyOtherClassList)
};
return MyClassRepository.GetAll(options)
}
And this would be the test:
[Fact]
public void GetAll_Success()
{
//Arrange
List<MyClass> expected = ... ;
var mockMyClassRepository = new Mock<IMyClassRepository>();
mockMyClassRepository.Setup(m => m.GetAll(It.IsAny<FindOptions<MyClass>>())).Returns(expected);
var myClassService = new MyClassService(mockMyClassRepository);
//Act
var result = myClassService.GetAll();
//Assert
Assert.Equal(expected, result);
mockMyClassRepository.Verify(m => m.GetAll(It.IsAny<FindOptions<MyClass>>()), Times.Once);
}
I want to avoid using It.IsAny for the FindOptions and be sure that the findOptions are excluding the MyOtherClassList attribute but I'm not sure how to compare the FindOptions class.
Any suggestion?
Hows this
mockMyClassRepository
.Setup(m => m.GetAll(It.IsAny<FindOptions<MyClass>>()))
.Returns(FindOptions<MyClass> a) =>
{
if (a.MyOtherClass != null || a.MyOtherClass.Length() > 0)
throw new InvalidOperationException();
else
return expected;
});
You can use an expression in the Returns to inspect the provided argument so you can apply what ever filtering you want.
mockMyClassRepository
.Setup(_ => _.GetAll(It.IsAny<FindOptions<MyClass>>()))
.Returns((FindOptions<MyClass> options) => {
var projection = options.Projection;
//...use as desired to filter the expected.
return expected;
});
The same could have been done in a Callback where the expected could be manipulated/altered, again based on the passed argument.
Do note that what ever is done with the passed argument is an implementation concern that applies to what is done by the actual repository, which you are trying to mock.
The only way to be sure that the findOptions are actually excluding the MyOtherClassList attribute would be via an integration test.
If you're looking to be more specific on the .Verify call, you can leave the .Setup call to use It.IsAny, then use additional constrains on the verify call, like this:
mockMyClassRepository.Verify(m => m.GetAll(It.Is<FindOptions<MyClass>>(options =>
options.Projection == .... //whatever logic would return true for your success criteria
)), Times.Once);

Am I writing my unit tests correctly? NUnit + NSubstitute

I recently started learning how to write unit tests, and what part of the unit to test for functionality and what to mock out. I'm using NSubstitute as my mocking framework. My example basically calls a repo class, which then makes an WEB API web call to an external service, ie: AddCreditCard, which then returns a result. I created 2 unit tests for AddCreditCard, one for Success, and one for Fail. I'm still not 100% sure I'm doing all of this correctly. The unit tests are passing, but im not sure if my Asserts are done on the correct data... All help and suggestions welcome!
public interface IApiResponse<T>
{
HttpStatusCode StatusCode { get; set; }
T Result { get; set; }
string ErrorMessage { get; }
bool HasErrors { get; }
}
public interface ISignedRequest
{
void ConfigureSettings(SignedRequestSettings settings);
IApiResponse Post(string jsonData, Dictionary<string, string> parameters = null,
IOptionalHeaders optionalHeaders = null);
}
public class PaymentRepository
{
private readonly SignedRequestSettings _settings;
private readonly ISignedRequest _signedRequest;
public PaymentRepository(ISignedRequest signedRequest = null)
{
if (signedRequest == null)
_signedRequest = new SignedRequest();
else
_signedRequest = signedRequest;
}
public IApiResponse AddCreditCard(CreditCard request)
{
var jsonData =
JsonConvert.SerializeObject(request);
string action = string.Format("customers/{0}/paymentmethods", request.ConnectId);
_settings.Url = string.Format("{0}{1}", String.Empty, action);
_signedRequest.ConfigureSettings(_settings);
return _signedRequest.Post(jsonData);
}
}
[Test]
public void AddCreditCard_GivenValidCreditCard_ReturnsCreatedResult()
{
//Given
var apiResponse = Substitute.For<IApiResponse>();
apiResponse.StatusCode = HttpStatusCode.Created;
var signedRequest = Substitute.For<ISignedRequest>();
signedRequest.Post(Arg.Any<String>()).Returns(apiResponse);
var creditCard = Substitute.For<CreditCard>();
creditCard.ConnectId = Guid.Parse("1fc1ad83-cd4e-4b68-bce6-e03ee8f47fb6");
var repo = new PaymentRepository(signedRequest);
//When
var addCreditCardResponse = repo.AddCreditCard(creditCard);
//Then
signedRequest.Received(1).ConfigureSettings(Arg.Any<SignedRequestSettings>());
signedRequest.Received(1).Post(Arg.Any<String>());
Assert.AreEqual(HttpStatusCode.Created, addCreditCardResponse.StatusCode);
}
[Test]
public void AddCreditCard_GivenInvalidCreditCard_ReturnsHasErrorsResult()
{
//Given
var apiResponse = Substitute.For<IApiResponse>();
apiResponse.HasErrors.Returns(true);
apiResponse.ErrorMessage.Returns("Add credit card error message");
var signedRequest = Substitute.For<ISignedRequest>();
signedRequest.Post(Arg.Any<String>()).Returns(apiResponse);
var creditCard = Substitute.For<CreditCard>();
creditCard.ConnectId = Guid.Parse("1fc1ad83-cd4e-4b68-bce6-e03ee8f47fb6");
var repo = new PaymentRepository(signedRequest);
//When
var addCreditCardResponse = repo.AddCreditCard(creditCard);
//Then
signedRequest.Received(1).ConfigureSettings(Arg.Any<SignedRequestSettings>());
signedRequest.Received(1).Post(Arg.Any<String>());
Assert.AreEqual(apiResponse.HasErrors, addCreditCardResponse.HasErrors);
Assert.AreEqual(apiResponse.ErrorMessage, addCreditCardResponse.ErrorMessage);
}
I think your tests are mostly OK, but there are some bits I would question. Both of your tests have these lines at the bottom:
signedRequest.Received(1).ConfigureSettings(Arg.Any<SignedRequestSettings>());
signedRequest.Received(1).Post(Arg.Any<String>());
Is it really important to those tests that these methods have been called on your signedRequest substitute? I'd suggest that it probably isn't. If these calls weren't made I'd argue that your test would have failed anyway (although this is to some extent a stylistic decision).
The second thing I'd say is that you're missing one or more tests (again the number is a bit stylistic). As it stands, you're not validating the information that's being supplied to the ConfigureSettings or Post calls on your signedRequest substitute. Your repository code could simply be doing _signedRequest.Post("some random string"); and your test would still pass. I'd add another test that validates these calls to ensure that the request is actually sent correctly. This is where the Received validation would make sense. Something like:
signedRequest.Received(1).ConfigureSettings(Arg.Is<SignedRequestSettings>(x=>x.Url == someHardCodedUrl));
signedRequest.Received(1).Post(someHardCodedJsonString);

Categories