2015-02-18 5 views
24

Poiché ho convertito i miei metodi WCF in Async, i miei test di unità non sono riusciti e non riesco a capire la sintassi corretta per farli funzionare.Moq con attività attende

Cllient classe proxy

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

classe di servizio

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 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'erro r è quando ClientProxy.DoSomething si chiama:

riferimento non impostato a un'istanza di un oggetto

I parametri sono entrambi bene.

Se converto il mio metodo ClientProxy.DoSomething ad un metodo sincrono (public void DoSomething(...)) il codice funziona bene, ma ho bisogno di questo per essere chiamato in modo asincrono

risposta

54

DoSomething rendimenti null invece di restituire un Task, e così si ottenere un'eccezione quando lo si aspetta. È necessario specificare quando si costruisce il mock che deve restituire un Task.

In questo caso sembra che si può semplicemente restituire un compito già completato utilizzando Task.FromResult così l'impostazione finto dovrebbe assomigliare a questa:

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

A partire dalla prossima versione di .Net (4.6) è possibile utilizzare Task.CompletedTask

+5

Va rilevato in questo caso, falso è solo un segnaposto, il valore falso è in realtà ignorato dal finto, ha solo bisogno di un compito, qualsiasi compito , completare. Potrebbe facilmente essere "foo" invece di false ... – mcse3010

+0

@ mcse3010 in effetti. – i3arnon

+1

Suvato per la sintassi 4.6, molto conciso e indicativo di ciò che effettivamente è. – starmandeluxe

4

Penso che è necessario restituire il Task dal DoSomething finto

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

Oppure il mio preferito è '.Returns (Task.Delay (0));' –

10

È possibile ridurre la quantità di disordine nel codice utilizzando ReturnsAsync

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

In questo modo è possibile rimuovere la parte Task.FromResult del codice