web-dev-qa-db-fra.com

Comment tester une méthode de classe privée dans Scala?

J'ai un objet compagnon avec une méthode privée, comme ceci:

package com.example.people

class Person(val age: Int)

object Person {
  private def transform(p: Person): Person = new Person(p.age + 1)
}

Je voudrais tester cette méthode, avec quelque chose comme:

class PersonSpec extends FlatSpec {

  "A Person" should "transform correctly" in {
    val p1 = new Person(1)
    val p2 = Person.transform(p1) // doesn't compile, because transform is private!
    assert( p2 === new Person(2) )
  }

}

Avez-vous de l'aide pour avoir accès aux méthodes privées du code de test?

En fait, comme il est écrit, je pourrais peut-être créer une sous-classe de Person, mais que faire si Person est déclaré comme final ou sealed?

Merci!

34
gdiazc

Je suis au milieu quand il s'agit de tout tester. Je ne teste généralement pas tout, mais il est parfois très utile de pouvoir tester une fonction privée sans avoir à manipuler mon code pour le rendre possible. Si vous utilisez ScalaTest, vous pouvez utiliser PrivateMethodTester pour le faire.

import org.scalatest.{ FlatSpec, PrivateMethodTester }

class PersonSpec extends FlatSpec with PrivateMethodTester {

  "A Person" should "transform correctly" in {
      val p1 = new Person(1)
      val transform = PrivateMethod[Person]('transform)
      assert(p2 === invokePrivate transform(p1))
    }
  }

Ce n'est peut-être pas exactement ce que vous voulez faire, mais vous avez l'idée.

57
jlegler

Vous pouvez déclarer que votre méthode est un package privé:

private[people] def transform(p: Person): Person = new Person(p.age + 1)

Si vous mettez PersonSpec dans le même package, il pourra y accéder.

Je vous laisse le soin de décider s'il est vraiment sage de tester une méthode privée :)

29
vptheron

La nécessité de tester les méthodes privées à l'unité est une odeur de conception.

Soit vous les testez via votre API publique, ce qui est correct si ce sont de petites méthodes d'assistance, soit, ce qui est plus probable, il contient une logique/responsabilité différente et doit être déplacé vers une autre classe qui est utilisée par délégation dans la personne. Ensuite, vous devez d'abord tester l'API publique de cette classe.

Voir un réponse connexe pour plus de détails.

Vous pouvez probablement y accéder en utilisant la réflexion Java/Scala, mais ce n'est qu'une solution de contournement pour le problème de conception. Néanmoins, si vous en avez besoin, voyez un connexe Java réponse comment faire cela .

8
Peter Kofler

Je ne pense pas que le test unitaire concerne le test du contrat de la classe - il s'agit de tester une fonctionnalité simple (unité).

De plus, je ne pense pas que ce soit une bonne idée de rendre certaines méthodes publiques uniquement pour les rendre facilement testables. Je crois que garder l'API aussi étroite que possible est un bon moyen d'aider d'autres développeurs à utiliser votre code (l'IDE ne proposera pas de méthodes privées) et à comprendre le contrat.

Nous ne devons pas non plus tout mettre dans une seule méthode. Donc, parfois, nous pouvons mettre de la logique dans une méthode privée ... et bien sûr, nous voulons également la tester. Le tester via l'API publique augmentera la complexité de votre test (une autre option est de déplacer la logique de la méthode privée vers une autre classe d'assistance et de la tester là-bas ... cette classe ne sera pas utilisée directement par les développeurs et n'encombrera pas l'api)

Les gars de scalatest, je pense, ont ajouté PrivateMethodTester dans un but.

1
Deil

Personnellement, je dis de rendre tout public et de simplement ajouter _ ou __ pour indiquer que les autres développeurs ne devraient pas l'utiliser.

Je me rends compte que c'est Scala et non Python, mais peu importe, "Nous sommes tous des adultes consentants ici."

Les méthodes "privées" ne sont pas réellement privées ( par exemple ) et ne sont certainement pas sécurisées, alors pourquoi rendre la vie plus difficile pour ce qui est essentiellement un contrat social? Préparez-vous et terminez - si un autre développeur veut aller fouiner dans des endroits sombres, il a une bonne raison ou mérite ce qu'il obtient.

1
MikeTwo

La réponse de @ jlegler ici m'a aidé, mais j'avais encore un débogage à faire avant que les choses fonctionnent, alors j'ai pensé écrire exactement ce dont j'avais besoin ici.

tester:

class A

object A {
  private def foo(c: C): B = {...}
}

utilisation:

val theFuncion = PrivateMethod[B]('foo)
val result = A invokePrivate theFunction(c)

Notez les emplacements de A, B

1
Dotan

De manière générale: si vous voulez tester efficacement votre code, vous premier devez l'écrire testable.

Scala implémente le paradigme fonctionnel et utilise largement des objets immuables par conception, les "classes de cas" en sont des exemples (à mon avis: la classe Person devrait être une classe de cas).

L'implémentation des méthodes privées a du sens si les objets ont un état mutable, dans ce cas, vous voudrez peut-être protéger l'état des objets. Mais si les objets sont immuables, pourquoi implémenter des méthodes comme privées? Dans votre exemple, la méthode produit une copie de Person, pour quelle raison souhaitez-vous la rendre privée? Je ne vois aucune raison.

Je vous suggère d'y penser. Encore une fois, si vous voulez tester efficacement votre code, vous devez l'écrire testable.

1
Marco

une solution possible serait de tester indirectement une méthode privée: tester une méthode publique qui appelle la méthode privée

0
Septem