Xunit: Moq Setup doesn't return expected value
Opened this issue · 3 comments
Hi, everyone.
I've implemented a service that sends sms to some devices by means of AWS SNS Service.
The service checks for each device if it exists in a whitelist table. If the device isn't found, the sms is sent to a fallback number.
So, I've a WhiteListUser class:
public class WhiteListUser
{
[Key]
public string Device { get; set; }
public string Username { get; set; }
public bool IsFallback { get; set; }
}
An application context :
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
{
}
public DbSet<WhiteListUser> WhiteListUsers { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<WhiteListUser>().HasData(new WhiteListUser
{
Device = "+39370XXXXXXX",
Username = "FALLBACK",
IsFallback = true
});
}
}
and the service:
public interface ISmsService
{
Task<IList<SendSmsResponse>> SendSmsAsync(SendSms @event, CancellationToken token);
}
public class SNSService : ISmsService
{
private readonly IAwsProvider _awsProvider;
private readonly ILogger<SNSService> _logger;
private readonly IHostEnvironment _environment;
private readonly IGenericRepository<WhiteListUser> _repository;
public SNSService(IHostEnvironment environment, IGenericRepository<WhiteListUser> repository, IAwsProvider awsProvider, ILogger<SNSService> logger)
{
_awsProvider = awsProvider;
_logger = logger;
_environment = environment;
_repository = repository;
}
public async Task<IList<SendSmsResponse>> SendSmsAsync(SendSms @event, CancellationToken cancellationToken)
{
if (_environment.IsDevelopment())
{
var fallbackNumber = (await _repository.Find(u => u.IsFallback)).First().Device;
int i = 0;
while (i < @event.Devices.Count)
{
var device = @event.Devices[i];
var users = await _repository.Find(u => u.Device == device);
if (users?.ToList().Count == 0)
{
@event.Devices[i] = fallbackNumber;
}
i++;
}
}
var response = await _awsProvider.SendSmsAsync(@event.Devices, @event.Message, cancellationToken);
return response;
}
}
SendSms event is a record with a message and a list of devices:
public record SendSms
{
public string Message { get; set; }
public List<string> Devices { get; set; }
}
When I try to run this test, it fails:
public class SNSServiceTest
{
private readonly ISmsService _sut;
private readonly Mock<IAwsProvider> _awsProviderMoq;
private readonly Mock<ILogger<SNSService>> _loggerMoq;
private readonly Mock<IHostEnvironment> _hostEnvironmentMoq;
private readonly Mock<IGenericRepository<WhiteListUser>> _repositoryMoq;
public SNSServiceTest()
{
_awsProviderMoq = new Mock<IAwsProvider>();
_loggerMoq = new Mock<ILogger<SNSService>>();
_hostEnvironmentMoq = new Mock<IHostEnvironment>();
_repositoryMoq = new Mock<IGenericRepository<WhiteListUser>>();
_sut = new SNSService(_hostEnvironmentMoq.Object,
_repositoryMoq.Object,
_awsProviderMoq.Object,
_loggerMoq.Object);
}
[Fact]
public async Task Does_Replace_Only_Not_In_Whitelist_Numbers_With_Fallback_In_Dev_Env()
{
// Arrange
var @event = new SendSms
{
Message = "pippo",
Devices = new List<string>
{
"unknown",
WhiteListFixtures.FallbackUser.Device,
WhiteListFixtures.WhiteListUser.Device
}
};
_repositoryMoq
.Setup(x => x.Find(c => c.IsFallback))
.ReturnsAsync(new List<WhiteListUser> { WhiteListFixtures.FallbackUser });
_repositoryMoq
.Setup(x => x.Find(c => c.Device == WhiteListFixtures.FallbackUser.Device))
.ReturnsAsync(new List<WhiteListUser> { WhiteListFixtures.FallbackUser });
_repositoryMoq
.Setup(x => x.Find(c => c.Device == WhiteListFixtures.WhiteListUser.Device))
.ReturnsAsync(new List<WhiteListUser> { WhiteListFixtures.WhiteListUser });
_hostEnvironmentMoq.Setup(x => x.EnvironmentName).Returns(Environments.Development);
// Act
await _sut.SendSmsAsync(@event, CancellationToken.None);
// Assert
_awsProviderMoq
.Verify(x => x.SendSmsAsync(
It.Is<IList<string>>(l => l.Count == 3 &&
l[0].Equals(WhiteListFixtures.FallbackUser.Device) &&
l[1].Equals(WhiteListFixtures.FallbackUser.Device) &&
l[2].Equals(WhiteListFixtures.WhiteListUser.Device)),
"pippo",
CancellationToken.None));
}
}
where Fixtures are:
public class WhiteListFixtures
{
public static WhiteListUser FallbackUser => new WhiteListUser
{
Device = _fallbackNumber,
Username = "Test1",
IsFallback = true,
};
public static WhiteListUser WhiteListUser = new WhiteListUser
{
Device = _whiteListNumber,
Username = "Test2",
IsFallback = false,
};
private static string _fallbackNumber = "+393701002134";
private static string _whiteListNumber = "+393702223344";
}
it seems that these two setups are not working, so devices are never found and all the devices are so replaced, also the third one that is in the whitelist:
_repositoryMoq
.Setup(x => x.Find(c => c.Device == WhiteListFixtures.FallbackUser.Device))
.ReturnsAsync(new List<WhiteListUser> { WhiteListFixtures.FallbackUser });
_repositoryMoq
.Setup(x => x.Find(c => c.Device == WhiteListFixtures.WhiteListUser.Device))
.ReturnsAsync(new List<WhiteListUser> { WhiteListFixtures.WhiteListUser });
this is the error message:
UnitTests.Services.SNSServiceTest.Does_Replace_Only_Not_In_Whitelist_Numbers_With_Fallback_In_Dev_Env
Source: SNSServiceTest.cs line 34
Duration: 11 ms
Message:
Moq.MockException :
Expected invocation on the mock at least once, but was never performed: x => x.SendSmsAsync(It.Is<IList>(l => ((l.Count == 3 && l[0].Equals(WhiteListFixtures.FallbackUser.Device)) && l[1].Equals(WhiteListFixtures.FallbackUser.Device)) && l[2].Equals(WhiteListFixtures.WhiteListUser.Device)), "pippo", CancellationToken)
Performed invocations:
MockIAwsProvider:2 (x):
IAwsProvider.SendSmsAsync(["+39370XXXXXXX", "+39370XXXXXXX", "+39370XXXXXXX"], "pippo", CancellationToken)
Stack Trace:
Mock.Verify(Mock mock, LambdaExpression expression, Times times, String failMessage) line 330
Mock1.Verify[TResult](Expression
1 expression) line 810
SNSServiceTest.Does_Replace_Only_Not_In_Whitelist_Numbers_With_Fallback_In_Dev_Env() line 67
--- End of stack trace from previous location ---
I don't know what am I doing wrong, if the test or the implementation.
I was expecting: IAwsProvider.SendSmsAsync(["+39370XXXXXXX", "+39370XXXXXXX", "+39370YYYYYYY"], "pippo", CancellationToken)
Can any expert help me?
Thanks in advance
Can someone help me???
@fsodano13, this issue tracker focuses on the development of Moq itself, not on fixing user code. Your query might meet a larger audience on sites such as Stack Overflow, or in Moq's Discord chat.