web-dev-qa-db-fra.com

Fichiers de simulation dans Java - Contenu de simulation - Mockito

Je suis assez nouveau dans la moquerie et j'ai essayé de simuler le contenu réel (essentiellement créer un fichier virtuel dans la mémoire seule) afin qu'aucune donnée ne soit écrite sur le disque à aucun moment.

J'ai essayé des solutions comme se moquer du fichier et se moquer du plus grand nombre de propriétés que je peux comprendre autant que possible, puis l'écrire avec un fichier ou un tampon, mais celles-ci ne fonctionnent pas bien, car elles ont besoin de canoniques chemins. Quelqu'un a trouvé une solution autre que celle-ci ou similaire, mais que j'approche mal de cela?

Je l'ai fait comme ça:

private void mocking(){
    File badHTML = mock(File.class);
    //setting the properties of badHTML
    when(badHTML.canExecute()).thenReturn(Boolean.FALSE);
    when(badHTML.canRead()).thenReturn(Boolean.TRUE);
    when(badHTML.canWrite()).thenReturn(Boolean.TRUE);
    when(badHTML.compareTo(badHTML)).thenReturn(Integer.SIZE);
    when(badHTML.delete()).thenReturn(Boolean.FALSE);
    when(badHTML.getFreeSpace()).thenReturn(0l);
    when(badHTML.getName()).thenReturn("bad.html");
    when(badHTML.getParent()).thenReturn(null);
    when(badHTML.getPath()).thenReturn("bad.html");
    when(badHTML.getParentFile()).thenReturn(null);
    when(badHTML.getTotalSpace()).thenReturn(0l);
    when(badHTML.isAbsolute()).thenReturn(Boolean.FALSE);
    when(badHTML.isDirectory()).thenReturn(Boolean.FALSE);
    when(badHTML.isFile()).thenReturn(Boolean.TRUE);
    when(badHTML.isHidden()).thenReturn(Boolean.FALSE);
    when(badHTML.lastModified()).thenReturn(System.currentTimeMillis());
    when(badHTML.mkdir()).thenReturn(Boolean.FALSE);
    when(badHTML.mkdirs()).thenReturn(Boolean.FALSE);
    when(badHTML.setReadOnly()).thenReturn(Boolean.FALSE);
    when(badHTML.setExecutable(true)).thenReturn(Boolean.FALSE);
    when(badHTML.setExecutable(false)).thenReturn(Boolean.TRUE);
    when(badHTML.setReadOnly()).thenReturn(Boolean.FALSE);

    try {
        BufferedWriter bw = new BufferedWriter(new FileWriter(badHTML));
        /*
          badHTMLText is a string with the contents i want to put into the file, 
          can be just about whatever you want
         */
        bw.append(badHTMLText);
        bw.close();

    } catch (IOException ex) {
        System.err.println(ex);
    }
}

Toutes les idées ou conseils seraient très utiles. Quelque part après cela, j'essaie essentiellement de lire le fichier en utilisant une autre classe. J'essaierais de simuler une sorte de flux d'entrée, mais l'autre classe ne prend pas de flux d'entrée, car c'est la classe de gestion io pour le projet.

27
Matej Voboril

Vous semblez avoir des objectifs contradictoires. D'une part, vous essayez d'éviter d'écrire des données sur le disque, ce qui n'est pas un mauvais objectif dans les tests. De l'autre, vous essayez de tester votre classe de gestion des E/S, ce qui signifie que vous travaillerez avec des utilitaires système qui supposent que votre File fonctionnera avec des appels natifs. En tant que tel, voici mes conseils:

  • N'essayez pas de vous moquer d'un File. Mais ne le fais pas. Trop de choses natives en dépendent.
  • Si vous le pouvez, divisez votre code de gestion des E/S en la moitié qui ouvre un File et le transforme en Reader, et la moitié qui analyse le HTML du Reader.
  • À ce stade, vous n'avez pas du tout besoin d'une maquette - construisez simplement un StringReader pour simuler la source de données.
  • Bien que cela gère assez bien vos tests unitaires, vous pouvez également vouloir écrire un test d'intégration qui utilise un fichier temporaire et vous assurer qu'il lit bien. (Merci Brice d'avoir ajouté cette astuce!)

N'ayez pas peur de refactoriser votre classe pour faciliter les tests, comme ici:

class YourClass {
  public int method(File file) {
    // do everything here, which is why it requires a mock
  }   
}   

class YourRefactoredClass {
  public int method(File file) {
    return methodForTest(file.getName(), file.isFile(),
        file.isAbsolute(), new FileReader(file));
  }   

  /** For testing only. */
  int methodForTest(
      String name, boolean isFile, boolean isAbsolute, Reader fileContents) {
    // actually do the calculation here
  }   
}   

class YourTest {
  @Test public int methodShouldParseBadHtml() {
    YourRefactoredClass yrc = new YourRefactoredClass();
    assertEquals(42, yrc.methodForTest(
        "bad.html", true, false, new StringReader(badHTMLText));
  }   
}   

À ce stade, la logique dans method est si simple qu'elle ne vaut pas la peine d'être testée, et la logique dans methodForTest est si facile d'accès que vous pouvez la tester intensivement.

63
Jeff Bowman

Une façon de se moquer des appels d'E/S (avec Java 7 ce serait la classe finale NIO Java.nio.file.Files)) Est d'encapsuler les appels nécessaires dans votre propre classe et de la simuler:

public class FileHelper {

    public Path createDirectory(String directoryName) throws IOException {
        return Files.createDirectory(Paths.get(directoryName));
    }

    public boolean exists(String name) throws IOException {
        return Files.exists(Paths.get(name), LinkOption.NOFOLLOW_LINKS);
    }

}

La logique métier se trouve dans le ImageManager:

FileHelper fileHelperMock = Mockito.mock(new FileHelper());
ImageManager imageManager = new ImageManagerImpl(fileHelperMock);

Le test vérifiera l'appel à la méthode createDirectory() sur votre maquette:

imageManager.save("directory");
Mockito.verify(fileHelperMock).createDirectory("directory");

J'utiliserais cette méthode pendant le développement piloté par les tests où je ne veux pas polluer les tests avec une gestion de fichiers réelle (par exemple, supprimer les répertoires/fichiers créés dans un bloc enfin dans chaque test unitaire).

Ensuite, j'aurais des tests d'acceptation couvrant tous les cas d'utilisation avec une véritable gestion des fichiers.

4
Tian Na