2016-08-17 5 views
1

I ont la fonction suivanteMVVM tests unitaires Dispatcher lumière

public void Reset() 
{ 
    DisableModule(); 
    DispatcherHelper.UIDispatcher.Invoke(() => 
    { 
      PixelPointInfoCollection.Clear(); 
      PixelPointInfoCollection.Add(new PointViewModel()); 
    }); 
    _cachedPoints.Clear(); 
} 

Le code suivant se coince dans la méthode Invoke(), lors de l'exécution d'un test unitaire.

J'ai vu quelques articles sur la création d'une interface personnalisée sur le répartiteur et le fait de se moquer du répartiteur dans les tests unitaires. par exemple http://blog.zuehlke.com/en/mvvm-and-unit-testing/

N'y a-t-il pas d'autre moyen? J'ai une énorme base de code. Ai-je vraiment besoin de tout changer maintenant?

Mise à jour 18/08/2016 Pour l'instant voici ce que je l'ai fait et il travaille

public static class AppServices 
{ 

    public static IDispatcher Dispatcher { get; set; } 
    public static void Init(IDispatcher dispatcher) 
    { 
     Dispatcher = dispatcher; 
    } 
} 

//this inteface is in order to overrcome MVVM light Dispatcher so we can mock it for unit tests 
public interface IDispatcher 
{ 
    void Invoke(Action action); 
    void Invoke(Action action, DispatcherPriority priority); 
    DispatcherOperation BeginInvoke(Action action); 
} 

public class DispatcherWrapper :IDispatcher 
{ 
    public DispatcherWrapper() 
    { 
     DispatcherHelper.Initialize(); 
    } 
    public void Invoke(Action action) 
    { 
     DispatcherHelper.UIDispatcher.Invoke(action); 
    } 

    public void Invoke(Action action, DispatcherPriority priority) 
    { 
     DispatcherHelper.UIDispatcher.Invoke(action, priority); 
    } 

    public DispatcherOperation BeginInvoke(Action action) 
    { 
     return DispatcherHelper.UIDispatcher.BeginInvoke(action); 
    } 
} 

donc à l'intérieur des App.xaml.cs j'appelle AppServices.Init (nouveau DispatcherWrapper());

et dans les tests unitaires j'appelle AppServices.Init (Substitute.For());

utilisant NSubstitute

S'il vous plaît commentaire si vous que je me manque quelque chose, je suis inquiet comment puis-je faire le cadre de moquerie pour exécuter les actions que je faisais à l'intérieur du

DispatcherHelper.UIDispatcher.Invoke 
+0

Malheureusement oui. Votre situation est le résultat d'un problème de conception initial qui a rendu la base du code difficile à tester unitaire. La difficulté à créer des tests unitaires pour le code est directement liée à la façon dont le code est conçu. L'article que vous avez mentionné dans votre message est ce que vous devez faire pour rendre l'accès au répartiteur factices car il (le répartiteur) est un problème d'implémentation associé au thread d'interface utilisateur qui ne sera pas disponible pendant vos tests unitaires. D'où le verrou sur 'Invoke' – Nkosi

Répondre

1

Malheureusement, la situation résulte d'un problème de conception initial qui a rendu la base du code difficile à tester. La difficulté à créer des tests unitaires pour le code est directement liée à la façon dont le code est conçu. L'article que vous avez mentionné dans votre message est ce que vous devez faire pour rendre l'accès au répartiteur factices car il (le répartiteur) est un problème d'implémentation associé au thread d'interface utilisateur qui ne sera pas disponible pendant vos tests unitaires. D'où le verrou sur Invoke

Pour citer l'article que vous avez mentionné:

Nous ne pouvons pas tester le code qui utilise App.Current.Dispatcher (depuis App.Current sera null au cours exécution de tests unitaires).

Une solution possible serait de créer une interface IDispatcher et une enveloppe autour App.Current.Dispatcher qui implémente cette interface.

public interface IDispatcher { 
    void Invoke(Action action); 
    void BeginInvoke(Action action); 
} 

public class DispatcherWrapper : IDispatcher { 
    Dispatcher dispatcher; 

    public DispatcherWrapper() {  
     dispatcher = Application.Current.Dispatcher;   
    } 
    public void Invoke(Action action) { 
     dispatcher.Invoke(action); 
    } 

    public void BeginInvoke(Action action) { 
     dispatcher.BeginInvoke(action); 
    } 
} 
+0

Vraiment, votre code de modèle de vue devrait déléguer l'exécution spécifique au thread à une implémentation de SynchronizationContext qui est injectée à l'exécution.Dans l'interface utilisateur, vous pouvez utiliser DispatcherSynchronizationContext et, dans les tests, vous pouvez hacher une implémentation qui exécute tout simultanément dans le thread en cours. – Will

+0

@Will Pouvez-vous s'il vous plaît donner une réponse complète avec un exemple? – Gilad

+0

@Gilad ce serait essentiellement cela, mais en utilisant SynchronizationContext au lieu de Dispatcher, et les noms de méthodes seraient différents. Ce n'est pas si complexe. – Will