Skip to content
This repository was archived by the owner on Jun 30, 2023. It is now read-only.
This repository was archived by the owner on Jun 30, 2023. It is now read-only.

Xunit: Moq Setup doesn't return expected value #101

@fsodano13

Description

@fsodano13

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](Expression1 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions