webentwicklung-frage-antwort-db.com.de

Moq mit Task warten

Seitdem ich meine WCF-Methoden in Async konvertiert habe, sind meine Komponententests fehlgeschlagen, und ich kann die richtige Syntax nicht finden, damit sie funktionieren.

Client-Proxy-Klasse

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

Serviceklasse

  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);
        }
    }

Testklasse

 /// </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();
    }

Der Fehler tritt auf, wenn ClientProxy.DoSomething Aufgerufen wird:

Der Objektverweis wurde nicht auf eine Instanz eines Objekts festgelegt

Die Parameter sind beide in Ordnung.

Wenn ich meine ClientProxy.DoSomething - Methode in eine synchrone Methode konvertiere (public void DoSomething(...)), funktioniert der Code einwandfrei, muss jedoch asynchron aufgerufen werden

41
jazza1000

DoSomething gibt null zurück, anstatt Task zurückzugeben, und Sie erhalten eine Ausnahme, wenn Sie darauf warten. Sie müssen beim Erstellen des Mocks angeben, dass ein Task zurückgegeben werden soll.

In diesem Fall können Sie eine bereits abgeschlossene Aufgabe einfach mit Task.FromResult Das Mock-Setup sollte also so aussehen:

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

Ab der nächsten Version von .Net (4.6) können Sie Task.CompletedTask so was:

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

Mit ReturnsAsync können Sie das Durcheinander im Code reduzieren

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

Auf diese Weise können Sie das Task.FromResult Teil des Codes

23
knorman

Ich denke, Sie müssen den Task vom DoSomething-Mock zurückgeben

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