Reputation: 651
My first foray into mocking in a WPF application. The code I'm testing is part of an MVVM ViewModel Method and looks like this:
try
{
var airframesForRegistration = this.UnitOfWork.Airframes.GetAirframesForRegistration(this.SearchRegistration);
this.currentAirframes = new ObservableCollection<Airframe>(airframesForRegistration);
}
catch (Exception ex)
{
this.logger.Error($"Could not access the database", ex);
throw;
}
I want to test that
To do this I'm using XUnit and Moq thus:
[Fact]
public void GetAirframesForSearchRegistration_DBAccessFail()
{
using (var mock = AutoMock.GetLoose())
{
mock.Mock<IUnitOfWork>()
.Setup(x => x.Airframes.GetAirframesForRegistration("AAAA"))
.Throws(new DataException());
string message = "Could not access the database";
DataException exception = new DataException();
mock.Mock<ILog>()
.Setup(x => x.Error(message, exception));
var afrvm = mock.Create<AirframesForRegistrationViewModel>();
afrvm.SearchRegistration = "AAAA";
Assert.Throws<DataException>(() => afrvm.GetAirframesForSearchRegistration());
mock.Mock<ILog>()
.Verify(x => x.Error(message, exception), Times.Exactly(1));
}
The test fails thus:
Message: Moq.MockException :
Expected invocation on the mock exactly 1 times, but was 0 times: x => x.Error("Could not access the database'", System.Data.DataException: Data Exception.)
Configured setups:
ILog x => x.Error("Could not access the database", System.Data.DataException: Data Exception.)
Performed invocations:
ILog.Warn("No Operators found in the database")
ILog.Warn("No airframe statuses found in the database")
ILog.Error("Could not access the database", System.Data.DataException: Data Exception.
at Moq.MethodCall.Execute(Invocation invocation) in C:\projects\moq4\src\Moq\MethodCall.cs:line 120
(NB The extra ILog warnings occur elsewhere in the ViewModel and I was expecting those).
Question
This implies that the error logging was invoked and yet the test fails because it was invoked zero times! How can Moq and XUnit be set up to correctly test for this scenario?
Upvotes: 1
Views: 198
Reputation: 247008
The setup of the logger arguments is the problem.
Differing instances between what is thrown and what is expected mean they wont match when the mock is invoked.
The mock unit of work is throwing a new exception. Not the one you are expecting.
[Fact]
public void GetAirframesForSearchRegistration_DBAccessFail() {
using (var mock = AutoMock.GetLoose()) {
//Arrange
DataException exception = new DataException();
mock.Mock<IUnitOfWork>()
.Setup(x => x.Airframes.GetAirframesForRegistration("AAAA"))
.Throws(exception);
string message = "Could not access the database";
mock.Mock<ILog>()
.Setup(x => x.Error(message, exception));
var afrvm = mock.Create<AirframesForRegistrationViewModel>();
afrvm.SearchRegistration = "AAAA";
//Act
Action act = () => afrvm.GetAirframesForSearchRegistration();
//Assert
Assert.Throws<DataException>(act);
mock.Mock<ILog>()
.Verify(x => x.Error(message, exception), Times.Exactly(1));
}
}
For a looser expectation you could have use It.IsAny<>
argument matchers
[Fact]
public void GetAirframesForSearchRegistration_DBAccessFail() {
using (var mock = AutoMock.GetLoose()) {
//Arrange
mock.Mock<IUnitOfWork>()
.Setup(x => x.Airframes.GetAirframesForRegistration("AAAA"))
.Throws(new DataException());
string message = "Could not access the database";
mock.Mock<ILog>()
.Setup(x => x.Error(message, It.IsAny<DataException>()));
var afrvm = mock.Create<AirframesForRegistrationViewModel>();
afrvm.SearchRegistration = "AAAA";
//Act
Action act = () => afrvm.GetAirframesForSearchRegistration();
//Assert
Assert.Throws<DataException>(act);
mock.Mock<ILog>()
.Verify(x => x.Error(message, It.IsAny<DataException>()), Times.Exactly(1));
}
}
Upvotes: 3