web-dev-qa-db-fra.com

Moq avec tâche attendent

Depuis que j'ai converti mes méthodes WCF en Async, mes tests unitaires ont échoué et je ne peux pas trouver la syntaxe correcte pour les faire fonctionner.

classe proxy Cllient

 public interface IClientProxy
{
     Task DoSomething(CredentialDataList credentialData, string store);
}

classe de service

  public class CredentialSync : ICredentialSync
{
    private ICredentialRepository _repository;

    private IClientProxy _client;

    public CredentialSync()
    {
        this._repository = new CredentialRepository();
        this._client = new ClientProxy();
    }

    public CredentialSync(ICredentialRepository repository, IClientProxy client)
    {
        this._repository = repository;
        this._client = client;
    }

   public async Task Synchronise(string payrollNumber)
    {
        try
        {
            if (string.IsNullOrEmpty(payrollNumber))
            {
                .... some code
              }
            else
            {
                CredentialDataList credentialData = new CredentialDataList();
                List<CredentialData> credentialList = new List<CredentialData>();

                // fetch the record from the database
                List<GetCredentialData_Result> data = this._repository.GetCredentialData(payrollNumber);
                var pinData = this._repository.GetCredentialPinData(payrollNumber);

                // get the stores for this employee
                var storeList = data.Where(a => a.StoreNumber != null)
                    .GroupBy(a => a.StoreNumber)
                    .Select(x => new Store { StoreNumber = x.Key.ToString() }).ToArray();

                var credential = this.ExtractCredentialData(data, pinData, payrollNumber);

                credentialList.Add(credential);
                credentialData.CredentialList = credentialList;

                foreach (var store in storeList)
                {       
                  //this line causes an Object reference not set to an instance of an object error
                   await  _client.DoSomething(credentialData, store.StoreNumber);

                }
            }
        }
        catch (Exception ex)
        {
            throw new FaultException<Exception>(ex);
        }
    }

Classe de test

 /// </summary>
[TestClass]
public class SynchTest
{

    private Mock<ICredentialRepository> _mockRepository;
    private Mock<IClientProxy> _mockService;

    [TestInitialize]
    public void Setup()
    {
       ... some setups for repository which work fine
    }

[TestMethod]      
    public async Task SynchroniseData_WithOneEmployee_CallsReplicateService()
    {
        this._mockService = new Mock<IClientProxy>();
        this._mockService.Setup(x=>x.DoSomething(It.IsAny<CredentialDataList>(), It.IsAny<string>()));
        // arrange
        string payrollNumber = "1";
        CredentialSync service = new CredentialSync(this._mockRepository.Object, this._mockService.Object);

        // act
        await service.Synchronise(payrollNumber);

        // assert                 
        this._mockService.VerifyAll();
    }

L'erreur est lorsque ClientProxy.DoSomething Est appelé:

La référence d'objet n'est pas définie à une instance d'un objet

Les paramètres sont tous les deux très bien.

Si je convertis ma méthode ClientProxy.DoSomething En une méthode synchrone (public void DoSomething(...)) le code fonctionne bien, mais j'ai besoin que cela soit appelé de manière asynchrone

41
jazza1000

DoSomething renvoie null au lieu de renvoyer un Task, et vous obtenez donc une exception en l'attendant. Vous devez spécifier lors de la construction de la maquette qu'elle doit renvoyer un Task.

Dans ce cas, il semble que vous pouvez simplement retourner une tâche déjà terminée en utilisant Task.FromResult donc la configuration simulée devrait ressembler à ceci:

this._mockService.Setup(...).Returns(Task.FromResult(false));

À partir de la prochaine version de .Net (4.6), vous pouvez utiliser Task.CompletedTask comme ça:

this._mockService.Setup(...).Returns(Task.CompletedTask);
84
i3arnon

Vous pouvez réduire l'encombrement dans le code à l'aide de ReturnsAsync

this._mockService.Setup(...).ReturnsAsync(false);

De cette façon, vous pouvez supprimer le Task.FromResult partie du code

23
knorman

Je pense que vous devez renvoyer le Task du DoSomething mock

this._mockService.Setup(x => x.DoSomething(It.IsAny<CredentialDataList>(), It.IsAny<string>()))
    .Returns(Task.FromResult<int>(0));
4
Ned Stoyanov