1

Frameworks

.NETCoreApp 1.1
EF Core 1.1.1
Xunit 2.2.0
Moq 4.7.8

Controller Post Method
_yourRepository is injected in the controllers constructor and is of type IYourRepository

[HttpPost(Name = "CreateMethod")]
public async Task<IActionResult> CreateMethod([FromBody] ObjectForCreationDto objectDto)
{
    if (objectDto== null)
    {
        return BadRequest();
    }

    if (!ModelState.IsValid)
    {
        return BadRequest();
    }

    await _yourRespository.CreateObject(objectDto);

    if (!await _yourRespository.Save())
    {
        throw new Exception("Creating this object failed on save.");
    }            

    return Ok();
}

Unit Test that fails

[Fact]
public async Task CreateObject_WhenGoodDtoReceived_SuccessStatusReturned()
{
    // Arrange
    var mockRepo = new Mock<IYourRepository>();
    var controller = new YourController(mockRepo.Object);
    var objectForCreationDto = new ObjectForCreationDto { Code = "0001", Name = "Object One" };

    // Act
    var result = await controller.CreateObject(objectForCreationDto);

    // Assert
    Assert.IsType<OkObjectResult>(result);
}

The test fails because the line

if (!await _yourRespository.Save())

Always evaluates to true. When it evaluates to true you can see that the code throws an error (which is handled by middleware)

_yourRepository.Save() method

public async Task<bool> Save()
{
    return (await _yourContext.SaveChangesAsync() >= 0);
}

I'm not sure how to solve the problem and I'm not 100% sure why it is failing.

Is it because the mocked IYourRepository interface doesn't include an implementation of the Save method?

If so, does that mean to test the Post method I would need to mock my DbContext and construct my YourRepository object using it?

Any explanation as to why this is failing and how to rectify it would be much appreciated

Nkosi
  • 235,767
  • 35
  • 427
  • 472
GreenyMcDuff
  • 3,292
  • 7
  • 34
  • 66
  • instead of `await _yourRespository.Save()` try `_yourRespository.Save().Wait()` – Anton Toshik Apr 21 '17 at 10:03
  • He is using a mock, _yourRepository has **no functionality**. That's the whole point of a mock, not to have a concrete implementation but to "fake" results you want it to return – Tseng Apr 21 '17 at 10:04

3 Answers3

2

You need to setup the repo to return a proper task from the async method. Moq allows this using ReturnsAsync

[Fact]
public async Task CreateObject_WhenGoodDtoReceived_SuccessStatusReturned()
{
    // Arrange
    var mockRepo = new Mock<IYourRepository>();
    mockRepo.Setup(_ => _.Save()).ReturnsAsync(true);//<-- ADD THIS
    var controller = new YourController(mockRepo.Object);
    var objectForCreationDto = new ObjectForCreationDto { Code = "0001", Name = "Object One" };

    // Act
    var result = await controller.CreateObject(objectForCreationDto);

    // Assert
    Assert.IsType<OkObjectResult>(result);
}
Nkosi
  • 235,767
  • 35
  • 427
  • 472
  • Thanks for the swift answers all. I'm still trying to get my head around this. If I'm testing the post method like this doesn't it mean that I'm not really checking if it will work? I could pass anything in (assuming it's a correct Dto) and the test will pass?? Or is testing the save behaviour something that should be saved (no pun intended!) for an integration test? – GreenyMcDuff Apr 21 '17 at 10:11
  • This is just one particular scenario which tests the controller's `CreateMethod` method. We mock the dependencies to exercise the method under test in isolation. – Nkosi Apr 21 '17 at 10:14
  • 1
    @GreenyMcDuff: You are testing your Controller action, not the repository. That's why you **mock** the repository to return the desired value. For example if you want to see Action returns OkObjectResult, you mock `Save()` to return true. If you want to test if it throw exception, you mock `Save()` to return `false` to simulate an db error without ever using the DB/DbContext itself. This is the whole point of mocks and **unit** test (unit test means: test a unit (method, class, property) without using external dependencies – Tseng Apr 21 '17 at 10:24
  • However your tests may be off, because in your controller action you are having ModelState validation (`ModelState.IsValid`) and I'm not certain that this will work, unless you do an integration test (using `TestServer` class), because the required services may not be set when you instantiate the `YourController` class manually w/o the Default controller Activator – Tseng Apr 21 '17 at 10:27
  • 1
    @Tseng thanks, that explains it a bit more. Agree with you on the `ModelState.IsValid` point. To test that I've just used `controller.ModelState.AddModelError("Error", "Model error");` in another test – GreenyMcDuff Apr 21 '17 at 13:53
1

Replace new Mock<IYourRepository>(); with new Mock<IYourRepository>(MockBehavior.Strict); - now it will throw exceptions when any method without Setup is called.

Obviously, you should not check (assert) something (like return values) you do not determined.

Dmitry
  • 16,110
  • 4
  • 61
  • 73
0

Mock Save method returns false by default. You need to explicitly setup true as return value:

mockRepo.Setup(x => x.Save()).Returns(Task<bool>.FromResult(true));
Ilya Chumakov
  • 23,161
  • 9
  • 86
  • 114