2017-02-20 1 views
0

Ich muss Unit-Test eine Methode, und ich möchte das Verhalten zu spotten, so dass ich den notwendigen Teil des Codes in der Methode testen kann.Abfangen von Objekt von der privaten Methode in einer öffentlichen Methode

Dafür möchte ich auf das Objekt zugreifen, das von einer privaten Methode innerhalb der Methode zurückgegeben wird, die ich versuche zu testen. Ich habe einen Beispielcode erstellt, um eine grundlegende Vorstellung davon zu geben, was ich erreichen möchte.

Main.class

Class Main { 
    public String getUserName(String userId) { 
    User user = null; 
    user = getUser(userId); 
    if(user.getName().equals("Stack")) { 
     throw new CustomException("StackOverflow"); 
    } 

    return user.getName(); 

} 

private User getUser(String userId) { 
    // find the user details in database 
    String name = ""; // Get from db 
    String address = ""; // Get from db 
    return new User(name, address); 
} 
} 

Testklasse

@Test (expected = CustomException.class) 
public void getUserName_UserId_ThrowsException() { 
    Main main = new Main(); 
    // I need to access the user object returned by getUser(userId) 
    // and spy it, so that when user.getName() is called it returns Stack 
    main.getUserName("124"); 
} 

Antwort

0

Sie können eine mockPrivate des PowerMock benutzen, aber ich würde es nicht empfehlen. Wenn Sie ein solches Problem haben, bedeutet das normalerweise, dass Ihr Design schlecht ist. Warum nicht die Methode geschützt?

+0

Ich würde nicht genau sagen, dass das Verbessern der Methode die Codequalität verbessert, eher den "Get from db" -Teil richtig zu verspotten. – skomp

+0

@Egor Ich bin mir nicht sicher, ob ich komplett folge, aber wie hilft mir die private Methode, einen Spion auf einem Benutzerobjekt zu erstellen? – bharathp

1

Es gibt nur zwei Möglichkeiten, privat Zugang:

  1. Reflexion mit
  2. den Anwendungsbereich
  3. vielleicht für Java 9 warten neue Mechanismen Umfang zu benutzen?

Ich würde den Gültigkeitsbereich Modifikator von privaten zu Paketbereich ändern. Die Verwendung von Reflektion ist nicht stabil für das Refactoring. Es spielt keine Rolle, ob Sie Helfer wie PowerMock verwenden. Sie reduzieren nur den Kachelcode um die Reflexion herum.

Aber der wichtigste Punkt ist, dass Sie nicht zu tief in Whitbox-Tests testen sollten. Dies kann dazu führen, dass das Testsetup explodiert. Versuchen Sie, Ihren Code in kleinere Teile zu zerlegen.

Die einzige Information, die die Methode "getUserName" vom User-Objekt benötigt, ist der Name. Es wird den Namen validieren und entweder eine Ausnahme auslösen oder sie zurückgeben. Daher sollte es nicht notwendig sein, ein Benutzerobjekt in den Test einzubringen.

Also mein Vorschlag ist, sollten Sie den Code retreiving den Namen aus dem User-Objekt in eine separate Methode extrahieren und diese Methode Paketbereich machen. Jetzt brauchen Sie kein Benutzer-Objekt mehr nur über das Hauptobjekt zu verspotten. Aber die Methode hat ihre minimalen Informationen verfügbar, um richtig zu funktionieren.

class Main { 

    public String getUserName(String userId) { 
     String username = getUserNameFromInternal(userId); 
     if (userName.equals("Stack")) { 
      throw new CustomException("StackOverflow"); 
     } 
     return user.getName(); 
    } 

    String getUserNameFromInternal(String userId) { 
     User user = getUser(userId); 
     return user.getName(); 
    } 

    ... 

} 

Der Test:

@Test (expected = CustomException.class) 
public void getUserName_UserId_ThrowsException() { 
    Main main = Mockito.mock(new Main()); 
    Mockito.when(main.getUserNameInternal("124")).thenReturn("Stack"); 
    main.getUserName("124"); 
} 
+0

Der Code in meinem Beitrag war nur ein Beispiel. Stellen Sie sich einen Fall vor, in dem getUserName das Benutzerobjekt auf verschiedene Arten verwendet. Nehmen wir an, wir rufen ungefähr 15 Methoden des Benutzerobjekts in getUserName auf. Aber ich möchte nur das Verhalten eines dieser Methodenaufrufe verspotten. Wie würde ich darüber gehen? – bharathp

+1

@bharathp Wenn ** eine ** Methode 15 andere Aufrufe auf irgendein Objekt macht, dann ist das wiederum ein Hinweis auf schlechtes Design und jemand, der nicht versteht, wie OO verwendet werden soll ;-) – GhostCat

+0

Wenn Sie 15 Methoden in getUserName aufrufen Dies hat keinen Einfluss auf die Methodik, um die Methode zu testen. Da @GhostCat jedoch darauf hinweist, dass dies ein Anzeichen für ein schlechtes Design ist, sollten Sie sich fragen, ob Sie das Prinzip der einheitlichen Verantwortung verletzen. Aber die Methode in Stücke zu schneiden und zu testen, bleibt der erste Schritt zu möglichen Refactorings. – oopexpert

1

Ihr Problem, das zu new in Ihrem privaten Methode aufrufen.

Und die Antwort lautet nicht, sich an PowerMock zu wenden; oder um die Sichtbarkeit dieser Methode zu ändern.

Die vernünftige Antwort ist, diese Abhängigkeit von "etwas, das mir ein Benutzerobjekt gibt" in seine eigene Klasse "zu extrahieren"; und stellen Sie Ihrer Klasse "Main" eine Instanz dieser Klasse zur Verfügung. Denn dann kannst du dieses "Fabrik" -Objekt einfach verspotten; und lass es tun, was immer du willst.

Bedeutung: Ihr aktueller Code ist einfach schwer zu testen. Anstatt die Probleme zu umgehen, die dadurch verursacht werden, investieren Sie Zeit in das Schreiben von leicht zu testendem Code. Zum Beispiel indem Sie diese videos als Ausgangspunkt betrachten.

Angesichts Ihres neuesten Kommentars: Wenn Sie mit Legacy-Code beschäftigen, dann sind Sie wirklich auf die Verwendung von PowerMockito. Der Schlüssel zum Verständnis: Sie "verspotten" diese private Methode nicht; Sie schauen eher in den Aufruf an new User() stattdessen spotten; wie beschrieben here.

+0

Könnten Sie bitte Ihren dritten Absatz näher erläutern? Leider ist der Code, den ich gerade teste, Legacy-Code und ich werde keine Zeit damit verbringen, ihn zu aktualisieren. Vielen Dank für den Link zur Playlist. – bharathp

+0

Was ich meinte ist: wenn Sie hier Ihren eigenen Code schreiben würden; dann möchtest du vielleicht zurücktreten und lernen ... wie man das "besser" macht. Wenn Sie jedoch mit vorhandenem Code arbeiten, den Sie nicht berühren können, ist PowerMock die einzige Antwort, die Sie dort haben. Siehe meine aktualisierte Antwort und ihren vierten Absatz ;-) – GhostCat

+0

Ich glaube nicht, dass es sich um Legacy-Code und "neuen" Code handelt. In diesem Fall handelt es sich um einen White-Box-Test oder einen Black-Box-Test. Die Frage ist: Interessieren Sie sich für den Kontrollfluss oder interessieren Sie sich für die Eingabeparameter, die bestimmte Ergebnisse erzeugen, ohne das WIE zu kennen? Wenn Ihnen der Kontrollfluss wichtig ist, kümmern Sie sich implizit um die Codequalität. Wenn Sie nur sicher sein wollen, dass eine Objektmethode Ergebnisse liefert, die mit einer Spezifikation übereinstimmen, gibt es kein echtes Handle, um die Codequalität zu erzwingen, da jeder Kontrollfluss zu einem "Implementierungsdetail" wird. – oopexpert

Verwandte Themen