2009-10-13 10 views
3

Je l'ai fait une programmation Scala, et je sais que, par exemple,Comment fonctionne la syntaxe ScalaTest?

xs map f 

est la même chose que

xs.map(f) 

, mais je ne sais pas comment généraliser cette syntaxe à quelque chose comme la syntaxe de ScalaTest, par exemple,

it should "throw NoSuchElementException if an empty stack is popped" in { 
    val emptyStack = new Stack[String] 
    evaluating { emptyStack.pop() } should produce [NoSuchElementException] 
} 

Je me demande surtout au sujet des choses qui ressemblent à des constructions de plusieurs mots, à savoir should produce. C'est propre.

+1

ScalaTest est open source, n'est-ce pas? Donc, si vous voulez savoir comment cela est fait, regardez simplement le code source. – Jesper

+2

Des choses comme les conversions implicites globales rendent difficile la définition des astuces de syntaxe. Mais si vous avez des idées spécifiques sur la façon de lire/parcourir, n'hésitez pas à partager. – Yang

Répondre

11

Ce type de syntaxe est la méthode des appels en notation opérateur, mais reportée de plus de trois jetons. Comme vous l'avez déjà mentionné:

xs map f 

signifie:

xs.map(f) 

Mais vous pouvez aller plus loin et dire:

xs map f map g 

qui signifie:

xs.map(f).map(g) 

En ScalaTest matchers, par exemple, vous pourriez dire:

result should not be null 

qui obtient désucré par le compilateur à:

result.should(not).be(null) 

Ce:

it should "throw an exception" in { ... } 

obtient Dessucré dans:

it.should("throw an exception").in { ... } 

Les accolades à la fin est vraiment juste un moyen de passer le code dans Betw Een les accolades (le code de test) dans la méthode in, enveloppé comme une fonction no-arg. Donc, tout cela est la même idée. La notation de l'opérateur est utilisée deux fois de suite.

Le dernier vous a demandé est un peu différent:

evaluating { ... } should produce [IllegalArgumentException] 

Cela se transforme en:

evaluating { ... } est, bien, d'abord évaluée, parce que les accolades donnent la priorité. Donc, c'est un appel de méthode, vous appelez une méthode nommée "evaluation", en passant le code entre les accolades comme une fonction sans-argument. Cela renvoie un objet, sur lequel devrait être invoqué. Ainsi devrait être une méthode sur l'objet retourné en invoquant l'évaluation. Ce qui devrait réellement prendre est le résultat de l'invocation du produit. Ici, produire est en fait une méthode qui a un paramètre de type tel que [IllegalArgumentException]. Cela doit être fait de cette façon afin que le compilateur Scala puisse "poor-man's-reify" ce paramètre de type. Il passe un paramètre "Manifest" implicite dans le produit qui peut fournir l'instance java.lang.Class pour IllegalArgumentException. Lorsque cette méthode devrait être invoquée, elle a donc une fonction contenant le code passé à l'évaluation, et un moyen de trouver le java.lang.Class du type d'exception mis entre crochets. Donc, il exécute le bloc de code enveloppé dans un essai, attrape l'exception, la compare avec ce qui est attendu. Si aucune exception n'est levée ou la mauvaise, la méthode should lève une exception TestFailedException. Sinon, la méthode devrait juste revenir silencieusement.

Donc, la réponse est que la ligne se désucré dans:

(evaluating { ... }).should(produce[IllegalArgumentException] (compilerSuppliedManifest)) 

Et la morale de l'histoire est que le code de haut niveau comme cela rend plus facile de voir l'intention du programmeur, mais souvent plus difficile de comprendre comment le code fonctionne réellement. La plupart du temps, dans la pratique, tout ce qui vous intéresse, c'est l'intention, mais de temps en temps, vous devez savoir comment quelque chose fonctionne. Dans de tels cas, dans Scala, vous pouvez passer -Xprint: typer comme argument de ligne de commande au compilateur Scala et il affichera une version de votre fichier une fois que toutes les opérations auront été effectuées. Vous pouvez donc voir ce qu'il y a quand vous en avez besoin.

2

C'est relativement simple car c'est un code normal - it should est équivalent à it.should donc il doit y avoir une valeur (ou une méthode) appelée it dans la portée. Et there is!

Cette variable est de type ItWord qui expose une méthode appelée should qui prend un objet de type BehaveWord. Ces coupleurs sont mélangés via des conversions implicites dans le ShouldMatcherstrait.

ScalaTest est réellement extrêmement bien documenté avec des tonnes d'exemples et des descriptions de la façon dont les choses fonctionnent.

+0

Merci pour le pointeur au bon endroit dans la documentation. Désolé, la question «ça devrait» était en fait évidente (je ne sais pas comment j'ai manqué cela), mais je suis toujours confus au sujet de '_ devrait produire _'. – Yang

Questions connexes