Nigdy nie rób tego drugiemy programiście, proszę. Załóżmy, że musisz się połączyć z zewnętrznym serwisem w kodzie. Dlaczego nie zrobić tego używając klasy ze zwykłymi metodami? Skąd pokusa, aby takie połączenie nawiązywać w statycznych metodach?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class AwsUtil {

  // ...

  public static Table getDynamoDbTable(String tableName) {
    DynamoDB dynamoDB = new DynamoDB(getDynamoClient());
    return dynamoDB.getTable(tableName);
  }

  public static AmazonDynamoDB getDynamoClient() {
    AmazonDynamoDB amazonDynamoDB = AmazonDynamoDBClientBuilder.standard().build();
    return amazonDynamoDB;
  }

}

Prawda, że to czyste wcielenie zła? Spotkanie wywołania takiego czegoś w kodzie boli… głównie jeśli chcemy napisać testy. Oczywiście da się z tego wybrnąć korzystając z kilku technik refaktoryzacyjnych. Możemy wynieść to wywołanie i schować za jakimś interfejsem opakowując zewnętrzne zależności w nasze klasy, tak jak opisałem to w tym artykule. Ale z poniższy kod przegina wszystkie możliwe granice dobrego smaku.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Service
@RequiredArgsConstructor
@Conditional(SimpleBeanEnabled.class)
public class SimpleSerive {
  //...
}

public class SimpleBeanEnabled implements Condition {

  @Override
  public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
    Table table = AWSUtil.getDynamoDbTable("anytable");
    //...
    return result;
  }
}

A, najważniejsza kwestia. Ten kod jest dostarczany jako JAR. Także nic nie można z nim zrobić, a trzeba go używać w kodzie z racji legacy. Więc powyższe sztuczki pewnie by pomogły, ale nie w sytuacji, gdy ten fragment kodu jest readonly. Więc jak tutaj napisać test integracyjny podnoszący kontekst Springa, ale bez łączenia się z AWS? Jedyne rozwiązanie jakie przyszło mi do głowy to klasę SimpleSerive schować za interfejsem, dorobić dla niego fake i w teście integracyjnym wykluczyć wsadzanie SimpleSerive do kontekstu, a w zamian niego załadować fake. Śmiga, ale trzeba założyć przysłowiowe “gacie przez głowę”.

Przez takie sytuacje na projekcie nie ma testów, a co dopiero mówić o tych na odpowiednim poziomie. Bo przecież “testu nie da się tutaj napisać” albo “jak powstanie to będzie bez sensu”. A to nie powinno się odwrócić zależności? Że przy pisaniu kodu produkcyjnego nie powinno się myśleć o jego testowalności? Odpowiedź jest raczej tylko jedna.