Vermeiden Sie spröde Tests für die Service-Schicht

Inhaltsverzeichnis

  • Link: #Übersicht[ 1. Übersicht]

  • link: #templates[ 2. Die Schichten]

  • link: #javaconfig[ 3. Motivation und Unschärfe der Linien der Einheit

Prüfung] link: #conclusion[ 4. ** Schlussfolgerung]

1. Überblick

Es gibt viele Möglichkeiten, die Service Layer einer Anwendung zu testen. Das Ziel dieses Artikels ist es, einen Weg aufzuzeigen, wie diese Ebene isoliert getestet werden kann, indem die Interaktionen mit der Datenbank vollständig verspottet werden.

In diesem Beispiel wird Spring für die Abhängigkeitsinjektion verwendet, JUnit, Hamcrest und Mockito zum Testen, aber die Technologien können variieren.

2. Die Schichten

Die typische Java-Webanwendung verfügt über eine Service-Schicht auf einer DAL/DAO-Schicht, die wiederum die rohe Persistenz-Schicht aufruft

1.1. Die Service Schicht

@Service
public class FooService implements IFooService{

   @Autowired
   IFooDAO dao;

   @Override
   public Long create( Foo entity ){
      return this.dao.create( entity );
   }

}

1.2. Die DAL/DAO-Schicht

@Repository
public class FooDAO extends HibernateDaoSupport implements IFooDAO{

   public Long create( Foo entity ){
      Preconditions.checkNotNull( entity );

      return (Long) this.getHibernateTemplate().save( entity );
   }

}

3. Motivation und Unschärfe der Linien des Unit-Tests

Beim Testen von Einheiten durch einen Service ist die Standardeinheit normalerweise die Serviceklasse ** . Beim Test wird die darunter liegende Schicht simuliert - in diesem Fall die DAO/DAL-Schicht, und die Wechselwirkungen darauf werden überprüft. Genau dasselbe für die DAO-Schicht: Die Interaktionen mit der Datenbank werden verspottet (in diesem Beispiel HibernateTemplate ) und damit die Interaktionen überprüft.

Dies ist ein gültiger Ansatz, führt jedoch zu spröden Tests. Das Hinzufügen oder Entfernen einer Ebene bedeutet fast immer, die Tests vollständig neu zu schreiben. Dies ist darauf zurückzuführen, dass die Tests auf die genaue Struktur der Schichten angewiesen sind und eine Änderung dazu eine Änderung der Tests bedeutet.

Um diese Art von Inflexibilität zu vermeiden, können wir den Geltungsbereich des Komponententests erweitern, indem wir die Definition der Einheit ändern. Wir können eine permanente Operation als Einheit betrachten, von der Service-Schicht über das DAO bis hin zum Raw-Tag Beharrlichkeit - was auch immer das ist. Nun wird der Komponententest die API der Service Layer verbrauchen und die reine Persistenz wird ausgeblendet - in diesem Fall das HibernateTemplate :

public class FooServiceUnitTest{

   FooService instance;

   private HibernateTemplate hibernateTemplateMock;

   @Before
   public void before(){
      this.instance = new FooService();
      this.instance.dao = new FooDAO();
      this.hibernateTemplateMock = mock( HibernateTemplate.class );
      this.instance.dao.setHibernateTemplate( this.hibernateTemplateMock );
   }

   @Test
   public void whenCreateIsTriggered__thenNoException(){
     //When
      this.instance.create( new Foo( "testName" ) );
   }

   @Test( expected = NullPointerException.class )
   public void whenCreateIsTriggeredForNullEntity__thenException(){
     //When
      this.instance.create( null );
   }

   @Test
   public void whenCreateIsTriggered__thenEntityIsCreated(){
     //When
      Foo entity = new Foo( "testName" );
      this.instance.create( entity );

     //Then
      ArgumentCaptor< Foo > argument = ArgumentCaptor.forClass( Foo.class );
      verify( this.hibernateTemplateMock ).save( argument.capture() );
      assertThat( entity, is( argument.getValue() ) );
   }

}

Jetzt konzentriert sich der Test nur auf eine einzige Verantwortung. Wenn die Erstellung ausgelöst wird, reicht die Erstellung der Datenbank an?

Beim letzten Test wird mithilfe der Mockito-Überprüfungssyntax überprüft, ob die save -Methode für die Hibernate-Vorlage aufgerufen wurde. Dabei wird das Argument im Prozess erfasst, sodass es ebenfalls überprüft werden kann. Die Verantwortung für die Erstellung der Entität wird über diesen Interaktionstest überprüft, ohne dass ein Zustand überprüft werden muss. Der Test geht davon aus, dass die Hibernate Save-Logik wie beabsichtigt funktioniert. Natürlich muss das auch getestet werden, aber das ist eine andere Verantwortung und eine andere Art von Tests.

4. Fazit

Diese Technik führt immer zu fokussierteren Tests, wodurch sie widerstandsfähiger und flexibler sind. Der einzige Grund, warum der Test jetzt fehlschlagen sollte, ist, dass die Verantwortlichkeit , die getestet wird, beschädigt ist.