2017-07-12 1 views
0

J'ai une application Scala qui utilise le modèle de gâteau:ScalaMock et le modèle de gâteau - Pourquoi mon talon n'est pas appelé?

trait RepositoryComponent { 
    def repository: Repository 

    trait Repository { 
    def shouldSave(record: GenericRecord): Boolean 
    def findRecord(keys: Array[String]): Long 
    def insertRecord(record: GenericRecord) 
    def updateRecord(keys: Array[String], record: GenericRecord) 
    def cleanUp() 
    } 
} 

trait DbRepositoryComponent extends RepositoryComponent with Logged { 
    val connection: Connection 
    val subscription: Subscription 
    val schema: Schema 

    def repository = new DbRepository(connection, subscription, schema) 

    class DbRepository(connection: Connection, subscription: Subscription, schema: Schema) extends Repository { 
    ... 
    } 

trait ConsumerComponent { 
    def consumer: Consumer 

    trait Consumer { 
    def write(keys: Array[String], record: GenericRecord) 
    def close() 
    } 
} 

trait DbReplicatorComponent extends ConsumerComponent with Logged { 
    this: RepositoryComponent => 

    def consumer = new DatabaseReplicator() 

    class DatabaseReplicator extends Consumer { 
    ... 
    } 

Quand j'ai commencé naïvement tester la mise en œuvre de consumer.write, j'ai essayé quelque chose comme ça (en utilisant ScalaMock):

class DbReplicatorComponentTests extends FunSuite with MockFactory { 

    private val schema = new Schema.Parser().parse(getClass.getResourceAsStream("/AuditRecord.avsc")) 
    private val record = new GenericRecordBuilder(schema) 
    .set("id1", 123) 
    .set("id2", "foo") 
    .set("text", "blergh") 
    .set("audit_fg", "I") 
    .set("audit_ts", 1498770000L) 
    .build() 

    test("A record should be inserted if the audit flag is 'I' and no existing record is found") { 
    val replicator = new DbReplicatorComponent with RepositoryComponent { 
     override def repository: Repository = stub[Repository] 
    } 

    (replicator.repository.shouldSave _).when(record).returns(true) 
    (replicator.repository.findRecord _).when(Array("123", "foo")).returns(0L) 

    replicator.consumer.write(Array("123", "foo"), record) 

    (replicator.repository.insertRecord _).verify(record) 
    } 
} 

Le test échoue parce que je ne stubbing l'implémentation réelle:

Unsatisfied expectation: 

Expected: 
inAnyOrder { 
    <stub-1> Repository.shouldSave({"id1": 123, "id2": "foo", "text": "blergh", "audit_fg": "I", "audit_ts": 1498770000}) any number of times (never called) 
    <stub-2> Repository.findRecord([Ljava.lang.String;@4b8ee4de) any number of times (never called) 
    <stub-4> Repository.insertRecord({"id1": 123, "id2": "foo", "text": "blergh", "audit_fg": "I", "audit_ts": 1498770000}) once (never called - UNSATISFIED) 
} 

Actual: 
    <stub-3> Repository.shouldSave({"id1": 123, "id2": "foo", "text": "blergh", "audit_fg": "I", "audit_ts": 1498770000}) 
ScalaTestFailureLocation: com.generalmills.datalake.sql.DbReplicatorComponentTests at (DbReplicatorComponentTests.scala:12) 
org.scalatest.exceptions.TestFailedException: Unsatisfied expectation: 

Ce problème met en évidence le fait que je fais vraiment pas groin Scala. Je n'ai aucune idée d'où provient stub-3. J'ai réussi à retravailler mes tests suite à la réponse de damirv en this question, mais ce que j'espère, c'est un peu de perspicacité pour m'aider à mieux comprendre pourquoi je ne suis pas en train de me moquer de ce que je pense du test ci-dessus?

Fwiw, je viens à Scala d'un fond C#.

MISE À JOUR: en fonction de la réponse ci-dessous, cela fonctionne:

test("A record should be inserted if the audit flag is 'I' and no existing record is found") { 
    val replicator = new DbReplicatorComponent with RepositoryComponent { 
     val _repo = stub[Repository] 
     override def repository: Repository = { 
     _repo 
     } 
    } 

    (replicator.repository.shouldSave _).when(record).returns(true) 
    (replicator.repository.findRecord _).when(Array("123", "foo")).returns(0L) 

    replicator.consumer.write(Array("123", "foo"), record) 

    (replicator.repository.insertRecord _).verify(record) 
    } 

Répondre

2

def repository: Repository ceci est une méthode qui retourne un nouveau bout à chaque fois que vous l'appelez. Essayez de faire un val au lieu de retourner le même talon sur chaque appel/accès:

test("A record should be inserted if the audit flag is 'I' and no existing record is found") { 
    val replicator = new DbReplicatorComponent with RepositoryComponent { 
    override val repository: Repository = stub[Repository] 
    } 
    // ... 
} 

, vous utilisez également un tableau comme type de paramètre. Notez que Array("foo") != Array("foo") dans scala alors je vous suggère d'utiliser le argThat matcher dans votre when appel où vous utilisez le Array (findRecord) - voir ici: Unable to create stub with Array argument in ScalMock

Ou essayez prédicats Matching, comme décrit ici: http://scalamock.org/user-guide/matching/

+0

Bien sûr - trop évident maintenant que vous l'avez souligné. – Stuart

+0

Bien que l'utilisation de val serait un problème, non? Ensuite, vous ne pouvez pas remplacer l'implémentation pour le test. – Stuart

+0

vous pouvez remplacer un 'def' avec un' val'. Donc: dans le trait, utilisez 'def'. Dans la classe/objet concret, utilisez 'val' et tout ira bien :) –