2016-09-15 1 views
1
public class Composer 
{ 
    private Task _ComposerTask;  
    private ConcurrentQueue<IValue> _Values; 
    public bool IsConnected { get; } 

    // Other dependencies 
    private IClient _Client; 
    private IWriter _Writer 

    public Task async ConnectAsync() 
    { 
     this.IsConnected = await _Client.ConnectAsync(); 
     _ComposerTask = Task.Run(() => this.Start()); 
    } 

    private void Start() 
    { 
     while(this.IsConnected) 
     { 
      IValue value; 
      if(_Values.TryDequeue(out value) == false) 
       continue; 

      _Writer.Write(value); 
     } 
    } 

    public void Send(IValue value) 
    { 
     _Values.Enqueue(value); 
    } 
} 

Lorsque vous êtes connecté classe Composer avec succès méthode d'exécution Start de manière asynchrone (sur un autre thread).
Start méthode vérifie la file d'attente des valeurs et l'envoie vers l'avant si la valeur existe.méthodes de tests unitaires sur un autre thread

Mon problème lors du test d'une méthode Send.

[Test] 
public void Send_ValidMessage_ExecuteWriteMethodWithGivenValue() 
{ 
    // Arrange 
    var fakeValue = Mock.Create<IValue>(); 
    var fakeWriter = Mock.Create<IWriter>(); 
    var fakeClient = Mock.Create<IClient>(); 

    Mock.Arrange(() => fakeClient.ConnectAsync().Returns(CompletedTask); 

    var composer = new Composer(fakeClient, fakeWriter); 

    // for (int i = 0; i < 10; i++) 
    // { 
    //  composer.Send(Mock.Create<IValue>()); 
    // } 

    composer.ConnectAsync().Wait(); 

    // Act 
    composer.Send(fakeValue); 

    // Assert 
    Mock.Assert(() => fakeWriter.Write(fakeValue), Occurs.Once()); 
} 

Avec commenté for loop test réussi. Mais si for loop exécuté et la file d'attente interne sera rempli avec même 10 valeurs avant la valeur ajoutée attendue, le test échoue avec le message: attendu au moins une fois, mais se produit 0 fois. Comme je comprends l'assertion se produit avant que la valeur a été mise en file d'attente par un autre thread, mais comment ce type de comportement peut-il être testé?

Répondre

0

Ma solution que je suis venu avec est classe refonte Composer ou être un changement plus spécifique Send méthode asynchrone:

public Task SendAsync(IValue value) 
{ 

} 

idée est derrière retour Task qui se termine lorsque la valeur donnée composée en avant sur « fond » fil.

Le test de l'unité ne nécessite que await jusqu'à la fin de la tâche et d'assurer une exécution correcte.

[Test] 
public async Task SendAsync_ValidMessage_ExecuteWriteMethodWithGivenValue() 
{ 
    // Arrange 
    var composer = TestFactory.GenerateComposer(); 

    // var tasks = new List<Task>(); 
    // for (int i = 0; i < 10; i++) 
    // { 
    //  tasks.Add(composer.SendAsync(Mock.Create<IValue>())); 
    // } 

    await composer.ConnectAsync(); 

    // Act 
    await composer.SendAsync(fakeValue); 

    // Assert 
    Mock.Assert(() => fakeWriter.Write(fakeValue), Occurs.Once()); 
} 

Mon test unitaire d'origine n'a pas réussi même sans valeurs supplémentaires ajoutées dans for loop. Test échoué de temps en temps s'il a été multiplié fois. Je pense que la raison en est le travail "imprévisible" du pool de threads.

Je ne sais toujours pas comment gérer le Task qui doit s'exécuter pendant la durée de vie complète de l'instance où il a été démarré, mais ce sera une autre question.

+1

Windows tests support async/await, au moins dans les versions actuelles de Visual Studio. Attendez juste les méthodes comme vous le feriez dans le code normal. faites en sorte que votre méthode de test fasse 'public async Task Send_ValidMessage_ExecuteWriteMethodWithGivenValue()' puis placez vos appels '.Wait()' dans 'await'. –

0

Vous devrez créer une sorte de "jointure". Quelque chose comme ceci devrait suffire:

public Task JoinAsync() 
{ 
    this.IsConnected = false; 
    return _ComposerTask; 
} 

Notez que vous devriez également utiliser ceci dans votre code de production. Si votre code n'observe finalement pas _ComposerTask, toutes les exceptions lancées par Start seront silencieusement avalées.

+0

Désolé, je me sens très incertain quand il faut ajouter un comportement à l'API publique de la classe uniquement à cause des tests. – Fabio

+0

@Fabio: Comme je l'ai noté dans ma réponse, ce code devrait également être utilisé ailleurs dans votre système. En règle générale, toutes les tâches doivent être attendues. –