I'm having an xUnit.net and Moq test problem, I have mocked my dependencies for my controller and setup the mock to return a Task<string> but my response.Value is null here. Can someone help with this problem?
namespace TestProject
{
public class CabinetControllerTest : ControllerBase
{
private Mock<IWearerPort> _canOpenPort;
private Mock<ITwoTraceLogger> _twoTraceLogger;
private Mock<ITransactionPort> _transaction;
public CabinetControllerTest()
{
_canOpenPort = new Mock<IWearerPort>();
_transaction = new Mock<ITransactionPort>();
_twoTraceLogger = new Mock<ITwoTraceLogger>();
}
[Fact]
public async void AccessControl_Success_Test()
{
//Arrange
Task<string> task = Task.FromResult<string>("Forbidden");
_canOpenPort.Setup(c =>
c.ValidateWearerAsync("BorrowCabinet",
"643674")).Returns(task);
var controller = new
CabinetController(_transaction.Object,
_canOpenPort.Object, _twoTraceLogger.Object);
var expected = Ok("Forbidden");
//Act
var response = await
controller.AccessControl("BorrowCabinet", "643674");
var actual = Ok(response.Value);
//Assert
Assert.Equal(expected, actual);
_canOpenPort.Verify(c =>
c.ValidateWearerAsync("BorrowCabinet", "643674"),
Times.Once);
}
Controiller:
[ApiController]
[Route("[controller]")]
public class CabinetController : ControllerBase
{
private readonly IWearerPort _canOpenPort;
private readonly ITwoTraceLogger _twoTraceLogger;
private readonly ITransactionPort _transaction;
public CabinetController(ITransactionPort transaction,
IWearerPort canOpen, ITwoTraceLogger twoTraceLogger)
{
_twoTraceLogger = twoTraceLogger;
_canOpenPort = canOpen;
_transaction = transaction;
}
[HttpGet]
[Route("[controller]/accesscontrol")]
public async Task<ActionResult<string>> AccessControl(string
clientID, string cardId)
{
_twoTraceLogger.LogTrace("CabinetController gets called");
var result = await
_canOpenPort.ValidateWearerAsync(clientID,cardId);
return Ok(result);
}
}
The test is configuring the Test Double to expect other values than the System Under Test (SUT) receives:
Make sure that those two pairs are the same. If the values don't match the
Setup, Moq will return default values, which often means null references.You may also want to use
ReturnsAsyncinstead ofReturnsto simplify the test code.After (ahem) actually having tried to repro the problem, here's a few things you can try.
Delete this line of code:
The object created by
Okdoesn't have structural equality anyway, so one can't use it to compareexpectedwithactual- unfortunately, because it's a good idea to use such comparisons for unit tests.Instead, write assertions like this:
I can't say that I entirely understand why
ActionResult<string>works the way it does, but it looks as though itsValueis null, whereas the associatedResultobject'sValueproperty is, indeed, populated by the appropriate value.In any case, testing like this risks turning into Framework Whac-A-Mole, so if possible, I'd recommend instead testing the API via HTTP.