Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Method without setup not return null #897

Closed
michelcedric opened this issue Aug 17, 2019 · 8 comments · Fixed by #900
Closed

Method without setup not return null #897

michelcedric opened this issue Aug 17, 2019 · 8 comments · Fixed by #900
Milestone

Comments

@michelcedric
Copy link

Hi,
I make a MockTestCase project : https://github.com/michelcedric/MockTestCase/blob/master/MockTest/UnitTest1.cs

When a method without setup is defined normaly he returns null.
But If the return it's a IEnumerable, it's return an empty list.

I think it's a strange behavior and I don't understand why not return null?

Best regards

Cédric Michel

@stakx

This comment has been minimized.

@stakx
Copy link
Contributor

stakx commented Aug 17, 2019

Apologies, I misread your above post. This doesn't look like a duplicate.

@michelcedric
Copy link
Author

Setups with no .Returns(…) nor .CallBase() no longer return default(T) for loose mocks, but a value that is consistent with the mock's CallBase and DefaultValue[Provider] settings.

#877
What do you hear by "but a value that is consistent with the mock's"?
Because in my test case if the return is a list it's return null but IEnumerable return an empty list,
And for me it's not consistent

@stakx
Copy link
Contributor

stakx commented Aug 17, 2019

What do you hear by [...]

As per my last post, please ignore my initial statement, I was mistaken.

As to your unit tests, your tests fail due to two basic misunderstandings on your part:

  • The DateTime test fails because the method you're setting up accepts a DateTimeOffset. While you are passing in DateTime.Now, that gets implicitly converted to DateTimeOffset by DateTimeOffset's implicit operator DateTimeOffset(DateTime). That's why your setup doesn't apply, a DateTimeOffset doesn't fulfill the It.IsAny<DateTime>() criterion.

  • You may expect a null return value when calling a mocked method with a return type of IEnumerable<>, but Moq's DefaultValue.Empty strategy generates empty enumerables for such return types. That's been like that for many years now.

    If you want Moq to produce null for such methods, you can hook up a different DefaultValueProvider:

    private class ZeroDefaultValueProvider : DefaultValueProvider
    {
        protected override object GetDefaultValue(Type type, Mock mock)
        {
            return type.IsValueType ? Activator.CreateInstance(type) : null;
        }
    }
    
    var mock = new Mock<T> { DefaultValueProvider = new ZeroDefaultValueProvider() };
    ...

@stakx stakx closed this as completed Aug 17, 2019
@stakx stakx added the question label Aug 17, 2019
@michelcedric
Copy link
Author

michelcedric commented Aug 17, 2019

var serviceMock = new Mock<IMeteringDataServiceAgent>();
serviceMock.Setup(s => s.GetDataList(It.IsAny<DateTime>())).Returns(_data);
var result3 = serviceMock.Object.GetDataList(new DateTime(2019,1,1));

//Expected the list but received null
//Because during the setup I put It.IsAny<DateTime>() and not It.IsAny<DateTimeOffset>()
Assert.AreEqual(_data, result3);

This unit test fail but in the setup I put DateTime and also during the invocation but it's fail?
It's because in my interface I defined a DateTimeOffset in parameter of GetDataList Method but why return null?
If the setup and invocation use the same type, normally it should work?

@stakx
Copy link
Contributor

stakx commented Aug 17, 2019

Like I already explained above: GetDataList never gets invoked with a DateTime but with a DateTimeOffset, therefore It.IsAny<DateTime>() doesn't match, therefore the whole setup including its Returns verb doesn't apply.

I suggest you read up on user-defined conversion operators and perhaps open up your code in a tool like ILDASM or ILSpy to see for yourself how DateTime.Now gets converted to a DateTimeOffset.

@michelcedric
Copy link
Author

michelcedric commented Aug 17, 2019

Why not raise an exception in this case instead of just return null?
Because Setup with It.IsAny<DateTime>() never working.
Best regards
Thanks

@stakx
Copy link
Contributor

stakx commented Aug 17, 2019

Why not raise an exception in this case [...]?

That is a very valid question, and a good idea! Ideally, such situations would be flagged by a Roslyn analyzer, but since we haven't (yet?) got one for Moq, we could try to produce a runtime error.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants