I\'m unit testing to see if a method is called.
[Fact]
public void Can_Save_Project_Changes()
{
//Arrange
var user =
I'm going to go a step further than Yoshi's comment.
The Performed invocations
message tells you the method was called but not with the parameters that you were verifying. My guess based on the messages is that there's something wrong with the first parameter.
You would need to post the test for me to be able to be more specific.
Update (after the Test was added)
Change userMgr.Setup
to return your 'user' variable, not a duplicate. Despite what I said earlier, this was the cause of your failure - the code being tested was being given a duplicate, and Moq was correctly saying that your method had not been called with user
because it had been called with the duplicate. So changing it to this fixes the problem:
userMgr.Setup(x => x.FindByNameAsync(It.IsAny<string>())).ReturnsAsync(user);
This could be made even stronger if the use of It.IsAny<string>()
can be avoided: if the specific string that is expected as a parameter is set up as part of the test setup, then give the value instead.
I suspect both of the "1" strings need to be identical to make this work, so rather than duplicate the string declare a local variable and use that instead of both strings.
I would suggest never using values like 1; prefer to randomly type something, so that it doesn't coincidentally pass. By which I mean, imagine a method which takes two integers as parameters: when calling Setup or Verify for that method, if you use the same value for both those integers, the test could pass even if your code has mistakenly swapped the values over (passing each into the wrong parameter). If you use different values when calling Setup or Verify, then it will only work when the correct value is passed in the correct parameter.
mockRepo.Setup
is redundant. Setup allows you to specify how the class behaves but there is nothing else after that on the line, so its redundant and can be removed. Some people use setup along with VerifyAll but you might want to read this discussion about using VerifyAll.
Now change your verify back to using project
rather than It.IsAny<Project>()
. I would expect it to work.
Update 2
Consider a tiled roof. Each tile is responsible for protecting one small part of the roof, slightly overlapping the ones below it. That tiled roof is like a collection of unit tests when using mocking.
Each 'tile' represents one test fixture, covering one class in the real code. The 'overlapping' represents the interaction between the class and the things it uses, which has to be defined using mocks, which are tested using things like Setup and Verify (in Moq).
If this mocking is done badly, then the gaps between the tiles will be big, and your roof could leak (i.e. your code might not work). Two examples of how mocking can be done badly:
It.IsAny
when you really don't need to.That last one is your biggest risk; but it's no different than the risk of writing bad unit tests (regardless of whether it involves mocking). If I wrote a unit test which exercised the code under test but then failed to make any assertions, or made an assertion about something that doesn't matter, that would be a weak test. Using It.IsAny
is like saying "I don't care what this value is", and means you're missing the opportunity to assert what that value should be.
There are times when it's not possible to specify the value, where you have to use It.IsAny
, and one other case I'll come back to in a second is also OK. Otherwise, you should always try to specify what the parameters are, either exactly, or at least using It.Is<T>(comparison lambda)
. The one other time it's ok to use It.IsAny<T>()
is when you are verifying that a call has not been made, using Times.Never
as a parameter to Verify
. In this case, it is usually a good idea to always use it, since it checks the call has not been made with any parameter (avoiding the possibility that you have simply made an error on what parameters are given).
If I wrote some unit tests which gave me 100% code coverage; but didn't test all the possible scenarios, that would be weak unit testing. Do I have any tests to try to find these badly written tests? No, and people who don't use mocking don't have tests like that either.
Going back to the tiled roof analogy... if I didn't have mocking, and had to test each part using the real dependencies here's what my roof would look like. I could have a tile for all of the bits at the bottom edge of the roof. No problem so far. For what would have been the next set of tiles up the roof, for what would have been one tile, I need a triangular tile, covering where that tile would have gone, and covering the tiles below it (even though they are already covered by a tile). Still, not too bad. But 15 tiles further up the roof, this is going to get exhausting.
Bringing that to a real world scenario, imagine I'm testing a client-side piece of code, which uses two WCF services, one of which is a third party that charges per use, one of which is protected by windows authentication, maybe one of those services has complex logic in its business layer before reaching the data layer and interacting with a database, and somewhere in there, I might have some caching. I daresay writing decent tests for this without mocking could be described as overly-convoluted, if it's even possible (in one person's lifetime)...
Unless you use mocking, which allows you to...
(For the record, speed of execution of the tests has never played any part in my decision to use mocking.)
Luckily mocking is simple, requiring barely any level of comprehension above what I have spelled out here. As long as you acknowledge that using mocking is a compromise compared to full-on integration testing, it yields the kind of savings in development and maintenance time that any product manager will be grateful for. So try to keep the gaps between your tiles small.
Try to setup your method like this:
mockRepo.Setup(m => m.SaveProject(It.IsAny(),It.IsAny())
And then verify using It.IsAny as well.
Or just use It.IsAny for the parameters you do not want (or cannot) check properly for some reason. You can also create custom matchers in the later case.
As mentioned in other comments. The problem is likely to be on the arguments that you have setup you mock to expect.