How can I check if a argument has certain propery values? - c#

I am trying to stub a RestClient to return a specific response for a specific request, which is recognized by checking the end of the URL that is used. This is the code I have come up with:
_clientStub = Substitute.For<IRestClient>();
_responseStub = Substitute.For<IRestResponse>();
_clientStub
.Get(
Arg.Is<IRestRequest>(
request => request.Resource.EndsWith("config.json")
)
)
.Returns(_responseStub);
I receive a NullReferenceException for the Arg.Is<IRestRequest> part and if I try to save it in a variable for reuse like this the variable evaluates to null:
protected static readonly IRestRequest CONFIG_QUERY =
Arg.Is<IRestRequest>(
request => request.Resource.EndsWith("config.json")
);
I was following the second example in the documentation so I am not exactly sure what is going wrong. Any help?
Clarification
For reproducability I created a minimal example:
[Fact]
public void StackOverflowTest()
{
RestSharp.IRestClient clientStub = Substitute.For<RestSharp.IRestClient>();
RestSharp.IRestResponse responseStub = Substitute.For<RestSharp.IRestResponse>();
clientStub
.Get(
Arg.Any<RestSharp.IRestRequest>()
)
.Returns(responseStub);
}
Yes, there are no assertions in this test. I don't even get to them anyways since the last command already throws and NullReferenceException. The interfaces are from RestSharp, but that should not really matter.
Update
To narrow the problem down I created an even simpler example and now it works:
public interface IStackOverflowResponse { };
public interface IStackOverflowRequest { };
public interface IStackOverflowClient
{
IStackOverflowResponse Get(IStackOverflowRequest request);
}
[Fact]
public void StackOverflowTest()
{
IStackOverflowClient clientStub = Substitute.For<IStackOverflowClient>();
IStackOverflowResponse responseStub = Substitute.For<IStackOverflowResponse>();
clientStub
.Get(
Arg.Any<IStackOverflowRequest>()
)
.Returns(responseStub);
}
So now I guess that there is a specific problem with mocking RestSharp.RestClient. I guess the problem lies with mocking/stubbing extension methods as the IRestClient does not have a Get method by itself, but instead there is an extension method for it.

The problem lies with the Get function of IRestClient... as it has none. This is just an extension method in RestClientExtensions. As you can see in the source code it simply calls Execute with Method.GET as parameter. So the correct way to stub
clientStub
.Get(
Arg.Any<RestSharp.IRestRequest>()
)
.Returns(responseStub);
is to do this:
clientStub
.Execute(
Arg.Any<RestSharp.IRestRequest>(),
Method.GET
)
.Returns(responseStub);

Related

How to get relevant error message when method called with unexpected parameter?

In Moq, I want to assert that a method TestMethod is called with a specific parameter. When the test fails, I want to see a useful error message similar to:
TestMethod called with unexpected value X for parameter P where Y expected.
public interface ITestObject
{
void TestMethod(int parameter);
}
Just for sake of illustration, I could achieve this using a handcoded mock as follows:
Assert.AreEqual failed. Expected:<3>. Actual:<2>. TestMethod called with unexpected value for parameter 'actual'.
public class MockTestMethodParameter : ITestObject
{
private readonly int expected;
public MockTestMethodParameter(int expected) { this.expected = expected; }
public void TestMethod(int actual)
{
Assert.AreEqual(actual, expected, $"{nameof(TestMethod)} called with unexpected value for parameter '{nameof(actual)}'.");
}
}
[TestMethod]
public void TestHandcodedMockFailure()
{
var mock = new MockTestMethodParameter(expected: 2);
mock.TestMethod(actual: 3);
}
My problem is, I can't figure out how to do this in Moq. When I set up my Mock and call Verify(), I get the following unexpected and unclear error message, instead of the message I was hoping for:
Expected invocation on the mock at least once, but was never performed: test => test.TestMethod(It.Is(2, GenericEqualityComparer))
Performed invocations:
MockUnitTest1.ITestObject:1 (test):
UnitTest1.ITestObject.TestMethod(3)
[TestMethod]
public void TestMoqFailure()
{
var expected = 2;
var actual = 3;
var mock = new Moq.Mock<ITestObject>(Moq.MockBehavior.Strict);
mock.Setup(test => test.TestMethod(Moq.It.IsAny<int>())).Verifiable();
mock.Object.TestMethod(actual);
mock.Verify(test => test.TestMethod(Moq.It.Is(expected, EqualityComparer<int>.Default)));
}
Granted, the information is there, but I expected something more along the lines of, "TestMethod was invoked, but with incorrect parameters." It confuses me when Moq reports that TestMethod was not invoked because, intuitively, I did invoke the mock. I called TestMethod with It.IsAny(), as declared in the mock setup.
I've tried many different adjustments, but none yielding the desired result:
Custom error message
Setup with Is(3)
Setup with Is(2)
MockBehavior.Loose
Different .NET platforms, Framework 4.6.1, Core 2.1, .NET 6.0
Is this simply the way Moq reports error results for Verify()? I'm new to Moq, and while this behavior is unexpected to me, perhaps it is working as designed.
This appears to be normal behavior for mock frameworks in general. Doing the same thing with Telerik JustMock yields a similar error message:
Occurrence expectation failed. Expected exactly 1 call. Calls so far: 0
[TestMethod]
public void TestJustMockFailure()
{
var expected = 2;
var actual = 3;
var mock = Mock.Create<ITestObject>(Behavior.Strict);
Mock.Arrange(() => mock.TestMethod(Arg.IsAny<int>()));
mock.TestMethod(actual);
Mock.Assert(() => mock.TestMethod(Arg.Is(expected)), Occurs.Once());
}
In summary:
The OP's use case (my use case) is valid, verifying a particular parameter value was passed to a mocked method. However, the error message does not report the parameter mismatch as such. Rather, it reports that the entire mocked method call did not occur. This is by design, and appears to be common among popular mock frameworks.

Mocking in Xunit- How to verify method that returns some value

I have created a unit test method that verifies method is called, below is the code for the same.The method builds email object and calls GeneratePDF method which returns bytes further BuildEmailInfo method returns email object.
public class SMTPEmailSender : IEmailSender
{
private IPDFCreater _pdfCreater;
public SMTPEmailSender(IPDFCreater pdfCreater)
{
_pdfCreater = pdfCreater;
}
public Email BuildEmailInfo(string sMTPServerUrl, FaxMailDTO faxAsMailRequest)
{
Email email=null;
try
{
var otp = new PDFData { OTP =faxAsMailRequest.OTP};
email = new Email
{
SMTPServerUrl = sMTPServerUrl,
Encoding = Encoding.UTF8,
ToAddress = faxAsMailRequest.ToEmailAddress,
ToAddressDisplayName = faxAsMailRequest.ToAddressDisplayName,
FromAddress = faxAsMailRequest.FromEmailAddress,
Subject = faxAsMailRequest.Subject,
Body = faxAsMailRequest.Body,
FromAddressDisplayName = faxAsMailRequest.FromAddressDisplayName,
ContentStream = new MemoryStream(_pdfCreater.GeneratePDF(otp)),
AttachmentName = faxAsMailRequest.FaxFileName
};
}
catch(Exception ex)
{
Log.Error("Method : BuildEmailInfo. Exception raised while building email data : {#Message}", ex.Message, ex);
}
return email;
}
Below is my unit test code , whenever I execute this it throws an error Expected invocation on the mock at least once, but was never performed: x=>x.GeneratePDF(pdfdata). Also Let me know if it is right way to perform the test
public class SMTPEmailSenderTest
{
private SMTPEmailSender _sMTPEmailSender;
Mock<IPDFCreater> _mockPdfCreator;
public SMTPEmailSenderTest()
{
_mockPdfCreator = new Mock<IPDFCreater>();
_sMTPEmailSender = new SMTPEmailSender(_mockPdfCreator.Object);
}
[Theory]
[MemberData(nameof(GetFaxAsMailObject))]
public void BuildEmailInfoTest_ReturnsValidEmailObject(FaxMailDTO faxMailDTO)
{
string smpturl = "localhost";
var otp = new PDFData { OTP = faxMailDTO.OTP };
var result = _sMTPEmailSender.BuildEmailInfo(smpturl, faxMailDTO);
_mockPdfCreator.Verify(x => x.GeneratePDF(otp));
}
}
This line:
_mockPdfCreator.Verify(x => x.GeneratePDF(otp));
performs a 'verification'. This is an assertion that checks if method .GeneratePDF has been called on _mockPdfCreator with otp as its parameter.
All .Verify methods from the interface of the Mock object are used to check if some method or property were called. You can also provide some filters to see if certain parameters were passed, for example:
_myMock.Verify(x => x.FooBar(5));
_myMock.Verify(x => x.FooBar(123));
_myMock.Verify(x => x.FooBar(It.IsAny<int>());
_myMock.Verify(x => x.FooBar(It.Is<int>(number => (number-5)%3 > 10));
all of these check if FooBar was alled on _myMock, but each of them looks only at calls that used certain values of parameters: 5, 123, anything-that-is-int, or (...).
You cannot use .Verify to check for the return value.
There's no such option there.
Why? Think about it. You've had:
_mockPdfCreator = new Mock<IPDFCreater>();
....
_mockPdfCreator.Verify(x => x.GeneratePDF(otp));
The _mockPdfCreator is your mock object. Not a real thing. It's a tiny ghost that acts as if it were some IPDFCreater.
There's not a slightest bit of a real implementation there.
How can you expect that GeneratePDF returns anythin meaningful?
It just won't. Nothing's there behind it. If anything called that method GeneratePDF, it would return NULL (or throw exception, depending on mocking mode: Loose/Strict).
...unless you SET UP your mock to do it differently:
var theThing = ...;
_mockPdfCreator = new Mock<IPDFCreater>();
_mockPdfCreator.Setup(x => x.GeneratePDF(It.IsAny<...>())).Returns(theThing);
....
// ... now check what `GeneratePDF` returned?!
Now of anything calls GeneratePDF method, it will return theThing. Alright.
But you knew that already. There's nothing to check. You set up GeneratePDF to return the thing, so there's not a slightest point to check what GeneratePDF returned. It's your mock and your setup!
Sooo, if anything called GeneratePDF, then NULL would be returned, because there's no setup for GeneratePDF. However, as Verify proved, the GeneratePDF was never called. This means that when you created the SMTPEmailSender giving it the mock as parameter:
_mockPdfCreator = new Mock<IPDFCreater>();
_sMTPEmailSender = new SMTPEmailSender(_mockPdfCreator.Object);
and then in test you've had:
....
var result = _sMTPEmailSender.BuildEmailInfo(smpturl, faxMailDTO);
_mockPdfCreator.Verify(x => x.GeneratePDF(otp));
then, apparently, the _sMTPEmailSender.BuildEmailInfo didn't fancy calling GeneratePDF at all.
Why? No idea. Most probably there was something either in smpturl or faxMailDTO that was considered invalid for this use case and that generate-pdf step was skipped. Check the Result. See if there are any errors or messages that would tell you anything about why it did not even try to call GeneratePDF.
Also note that the verification you wrote were
x => x.GeneratePDF(otp)
That's pretty specific. It has hard-coded reference to otp. So maybe it was called, but with different parameter value?
Try adding:
var result = _sMTPEmailSender.BuildEmailInfo(smpturl, faxMailDTO);
_mockPdfCreator.Verify(x => x.GeneratePDF(It.IsAny<PDFData>())); // <-
_mockPdfCreator.Verify(x => x.GeneratePDF(otp));
or something along the lines and see which Verify fails. If the former passes and the latter fails, than everything's mostly fine, it's just not the exact OTP that you expected (maybe _sMTPEmailSender cloned it? etc).
In any chance that the former fails, then it means that GeneratePDF is truly not called even a single time, and then it means you have to learn why BuildEmailInfo with params (smpturl, faxMailDTO) doesn't do what you expect. You've got a try-catch-log there. Maybe some nullreferenceexption? But I doubt it.
You've got there:
[MemberData(nameof(GetFaxAsMailObject))] /// <==== B
public void BuildEmailInfoTest_ReturnsValidEmailObject(FaxMailDTO faxMailDTO) // <--- A
{
...
var otp = new PDFData { OTP = faxMailDTO.OTP }; //<--- C
...
_mockPdfCreator.Verify(x => x.GeneratePDF(otp)); //<---D
So, the faxMailDTO is from GetFaxAsMailObject. The BuildEmailInfo gets it via params and passes part of it to GeneratePDF. Then you assert in Verify that D uses newly-constructed otp from line C. That just can't work. The faxMailDTO from A+B so from GetFaxAsMailObject certainly DOES NOT contain otp from C and certainly will not pass otp object to GeneratePDF. The GeneratePDF will get some other PDFData object that came from faxMailDTO from A+B.
I think I've said enough and covered all issues with your test setup.. You almost have it right. Good luck!
Verifications on mock objects should be your 'last resort' in unit tests. Think about it: is an actual requirement violated if the PDF creator does not call the GeneratePDF method? The user only cares that the PDF was generated.
In this case, you can verify the outcome of the BuildEmailInfo method directly, for example:
var result = _sMTPEmailSender.BuildEmailInfo(smpturl, faxMailDTO);
var expectedBytes = ...; // TODO - get the expected byte[] array from somewhere
Assert.Equal(expectedBytes, result.ContentStream.ToArray());
Additionally, you may be able to write this test without mocking the dependency at all? If the actual PDF creator object can be called to generate a byte[] array in memory, you could just use the real object instead of mocking it.

Is Moq incompatible with PrivateObject?

I'm trying to test a private method on a mocked object. Please, calm down, I know you're getting your pitchforks out.
I'm well aware everything about to say can be answered by yelling REFACTOR at me. I just need a straight answer. Someone look me in the eyes and tell me this can't be done. It's an ungoogleable problem, so I just need to hear it.
Here's what I'm dealing with.
public class SecretManager
{
protected virtual string AwfulString { get { return "AWFUL, AWFUL THING"; }
public SecretManager()
{
//do something awful that should be done using injection
}
private string RevealSecretMessage()
{
return "don't forget to drink your ovaltine";
}
}
Here's me trying to test it.
var mgr = new Mock<SecretManager>();
mgr.Protected().SetupGet<string>("AwfulThing").Returns("");
var privateObj = new PrivateObject(mgr.Object);
string secretmsg = privateObj.Invoke("RevealSecretMessage");
Assert.IsTrue(secretmsg.Contains("ovaltine"));
and the exception:
System.MissingMethodException: Method 'Castle.Proxies.SecretManagerProxy.RevealSecretMessage' not found
Is what I'm trying to do, mad as it is, possible? Or is this simply too much hubris for a unit test to bear?
You're trying to call a method on the proxy that Castle created. The proxy won't have access to the private method on the class that it inherits from because, well, the method is private. Remember that Castle.Proxies.SecretManagerProxy is actually a subclass of SecretManager.
Do you really need a mock of SecretManager? I realize your code is a slimmed down abstract of the real code, but it seems the only thing you're doing with the mock is setting up a return for a property that isn't used by the method you're trying to test anyway.
var privateObj = new PrivateObject(mgr.Object, new PrivateType(typeof(SecretManager)));
string secretmsg = privateObj.Invoke("RevealSecretMessage");
It will work by specifying PrivateType for the PrivateObject.
Your code should be following for what you are trying to test. You don't need to mock the SecretManager and SetGet "AwfulThing" as you are not using it.
var privateObj = new PrivateObject(new SecretManager());
string secretmsg = (string)privateObj.Invoke("RevealSecretMessage", new object[] { });
Assert.IsTrue(secretmsg.Contains("ovaltine"));
But ideally you shouldn't be testing Private methods. See below article for the explanation:
http://lassekoskela.com/thoughts/24/test-everything-but-not-private-methods/

How do I get arguments passed to a data access layer that uses System.Action as an input parameter?

I'm trying to create some unit tests for an application I've recently inherited. Currently using NSubstitute because that's what the previous programmer used, but I'm not attached to it.
The method I'm testing calls the DataService class' Create method.
Calling Create Method
var contactProductLink = this.dsService.Create<ContactProductLink>(x =>
{
x.ContactRoleId = prod.RoleId;
x.ContactId = contactViewModel.ContactId;
x.ProductId = prod.ProductId;
x.Active = true;
x.InsertDate = DateTime.Now;
x.InsertUserId = user.employeeId;
x.UpdateDate = DateTime.Now;
x.UpdateUserId = user.employeeId;
});
DataService Create Method:
public TEntity Create<TEntity>(Action<TEntity> propertySetter = null) where TEntity : class
{
var tEntity = this.Context.Create<TEntity>();
if (propertySetter != null)
{
propertySetter(tEntity);
}
return tEntity;
}
The approach I've taken (and maybe there's a better way) is to use NSubstitute to mock the DataService. When I'm doing my assertions at the end, I'm checking to make sure that the Create method was called:
mockDataSupplierService.Received().Create<ContactProductLink>(Arg.Any<Action<ContactProductLink>>());
However, I'd like to also verify the input that was sent to the method is correct, and here's where I'm running into trouble. I can get the System.Action object that was passed to the Create method, but I can't figure out how to pull out the parameters (such as ContactRoleId, ContactId, etc. as posted in the calling create method code snippet).
So after all of that what I'm asking is:
How can I access those input parameters so I can verify the correct arguments are being passed to the data service? Is it even possible?
Is there a better way to do this than what I'm currently trying to do?
Solution
//Arrange
mockDataSupplierService.Create<ContactProductLink>(Arg.Do<Action<ContactProductLink>>(x=> actionToPopulateEntity = x));
//Assert
mockDataSupplierService.Received().Create<ContactProductLink>(Arg.Any<Action<ContactProductLink>>());
var entity = new ContactProductLink();
actionToPopulateEntity.Invoke(entity);
Assert.AreEqual(ExpectedContactId, entity.ContactId);
How can I access those input parameters so I can verify the correct arguments are being passed to the data service? Is it even possible?
Essentially you can't, as it is not possible to extract "code" details from action (consider what happens when you pass an action that doesn't set any properties - this is totally legal, but would break hypothetical mechanism).
However, you can try this instead:
Create entity with initial values
Use Arg.Invoke argument, telling NSubstitute to use chosen object as action parameter
Verify that entity properties values changed
For example:
// Arrange
var entity = new ContactProductLink
{
ContactRoleId = // ...
// ...
};
mockDataSupplierService
.Create<ContactProductLink>(Arg<ContactProductLink>.Invoke(entity));
// Act
// ...
Assert.That(entity.ContactRoleId, Is.EqualTo(2));
// ...

rhinomocks setting expectation, unit test always passes

I'm trying to become more familiar with the Rhinomocks framework, and I'm trying to understand the Expect methods of rhinomocks.
Here's a unit test I have written:
[TestMethod]
public void Create_ValidModelData_CreatesNewEventObjectWithGivenSlugId()
{
//Arrange
var eventList = new List<Event>() { new Event() { Slug = "test-user" } };
_stubbedEventRepository.Stub(x => x.GetEvents())
.Return(eventList);
_stubbedEventRepository
.Expect(x => x.SaveEvent(eventList.SingleOrDefault()))
.Repeat
.Once();
var controller = new EventController(_stubbedEventRepository);
EventViewModel model = new EventViewModel();
//Act
//controller.Create(model); COMMENTED OUT
//Assert
_stubbedEventRepository.VerifyAllExpectations();
}
I thought I understood this code to only pass if the SaveEvent(...) method get's called exactly once. However, with controller.Create(model) commented out, the test still passes. Inside controller.Create(model) is where the SaveEvent() method gets called.
I tried the following:
_stubbedEventRepository
.Expect(x => x.SaveEvent(eventList.SingleOrDefault()));
But it still passes every time, so what am I doing incorrectly stack overflow? The sources I have looked at online haven't been able to help me. Why is VerifyAllExpectations() yielding a successful unit test?
Thank you!
Here's the body of the controller constructor:
public EventController(IEventRepository eventRepository)
{
_eventRepository = eventRepository;
}
edit:
// member variables
private IEventRepository _stubbedEventRepository;
[TestInitialize]
public void SetupTests()
{
_stubbedEventRepository = MockRepository.GenerateStub<IEventRepository>();
}
If you want to verify the behavior of the code under test, you will use a mock with the appropriate expectation, and verify that. If you want just to pass a value that may need to act in a certain way, but isn't the focus of this test, you will use a stub.

Categories

Resources