risorse | good unit tests

Good Unit Tests /13

Questa parte (la tredicesima; qui la prima, qui la seconda, qui la terza, qui la quarta, qui la quinta, qui la sesta, qui la settima, qui l'ottava, qui la nona, qui la decima, qui l'undicesima e qui la dodicesima) descrive un piccolo refactoring della classe Boolean.

La serializzazione del messaggio che descrive la ragione del fallimento del test avviene per mezzo di un oggetto temporaneo TextFlow:

// file are-almost-equal.cpp

#include "gut.h"
#include <cmath>

const double pi      = 3.141592653;
const double epsilon = 0.001;

gut::Boolean AreAlmostEqual(double d1, double d2) {
  double diff = std::fabs(d1 - d2);
  return gut::Boolean(
    diff < epsilon,
    gut::TextFlow()
      << "|"
      << d1
      << " - "
      << d2
      << "| = "
      << diff
      << " >= "
      << epsilon);
}

TEST("AreAlmostEqual") {
  CHECK(AreAlmostEqual(pi, 3.14));
  CHECK(AreAlmostEqual(pi, 3.141));
}

/* output:
 *
 * Test suite started...
 * AreAlmostEqual: FAILED
 *  are-almost-equal.cpp(12) : [error] AreAlmostEqual(pi, 3.14) evaluates to
 *  |3.14159 - 3.14| = 0.00159265 >= 0.001
 * Ran 1 test(s) in 0s.
 * FAILED - 1 failure(s) in 1 test(s).
 */

L'intenzione è di eliminare l'uso dell'oggetto TextFlow e serializzare il messaggio direttamente in Boolean:

gut::Boolean AreAlmostEqual(double d1, double d2) {
  double diff = std::fabs(d1 - d2);
  return gut::Boolean(diff < epsilon)
    << "|"
    << d1
    << " - "
    << d2
    << "| = "
    << diff
    << " >= "
    << epsilon);
}

La nuova forma è più chiara, oltre che più compatta. Il risultato è ottenuto integrando in Boolean il comportamento di TextFlow:

// file gut.h
// ...

class Boolean {
  bool value_;
  std::string repr_;
  mutable std::stringstream repr_;
public:
  Boolean(bool value) : value_(value), repr_(gut::toString(value)) { }
  Boolean(bool value, const std::string& repr) : value_(value), repr_(repr) { }
  Boolean(const Boolean& other) : value_(other.value_) {
    repr_ << other.repr_.rdbuf();
  }
  explicit operator bool() const {
    return value_;
  }
  operator std::string() const {
    if (!repr_.rdbuf()->in_avail())
      repr_ << std::boolalpha << value_;
    return repr_.str();
  }
  template <typename T>
  Boolean& operator<<(const T& item) {
    repr_ << item;
    return *this;
  }
};

class TextFlow {
  std::ostringstream oss_;
public:
  template <typename T>
  TextFlow& operator<<(const T& item) {
    oss_ << item;
    return *this;
  }
  operator std::string() const {
    return oss_.str();
  }
};

// ...

Codice sorgente

Pagina modificata il 29/10/2014