validating - Test-Driven Development

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wangbingfengf98/article/details/88934439

The premise of Test-Driven Development(TDD) is that if we design and write our code with testing in mind, we not only create testable code, it will also be better designed. In general, this seems to hold true. If I'm thinking "how will I test this?" it makes my code different, and oftentimes "testable" translates to "usable".

Example:

interface

// validating/StringInverter.java
// (c)2017 MindView LLC: see Copyright.txt
// We make no guarantees that this code is fit for any purpose.
// Visit http://OnJava8.com for more book information.
package validating;

interface StringInverter {
  String invert(String str);
}

four implement classes 

// validating/Inverter1.java
// (c)2017 MindView LLC: see Copyright.txt
// We make no guarantees that this code is fit for any purpose.
// Visit http://OnJava8.com for more book information.
package validating;

public class Inverter1 implements StringInverter {
  public String invert(String str) {
    return str;
  }
}
// validating/Inverter2.java
// (c)2017 MindView LLC: see Copyright.txt
// We make no guarantees that this code is fit for any purpose.
// Visit http://OnJava8.com for more book information.
package validating;

import static java.lang.Character.*;

public class Inverter2 implements StringInverter {
  public String invert(String str) {
    String result = "";
    for (int i = 0; i < str.length(); i++) {
      char c = str.charAt(i);
      result += isUpperCase(c) ? toLowerCase(c) : toUpperCase(c);
    }
    return result;
  }
}
// validating/Inverter3.java
// (c)2017 MindView LLC: see Copyright.txt
// We make no guarantees that this code is fit for any purpose.
// Visit http://OnJava8.com for more book information.
package validating;

import static java.lang.Character.*;

public class Inverter3 implements StringInverter {
  public String invert(String str) {
    if (str.length() > 30) {
      throw new RuntimeException("argument too long!");
    }
    String result = "";
    for (int i = 0; i < str.length(); i++) {
      char c = str.charAt(i);
      result += isUpperCase(c) ? toLowerCase(c) : toUpperCase(c);
    }
    return result;
  }
}
// validating/Inverter4.java
// (c)2017 MindView LLC: see Copyright.txt
// We make no guarantees that this code is fit for any purpose.
// Visit http://OnJava8.com for more book information.
package validating;

import static java.lang.Character.*;

public class Inverter4 implements StringInverter {
  static final String ALLOWED = "abcdefghijklmnopqrstuvwxyz ,." + "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

  public String invert(String str) {
    if (str.length() > 30) {
      throw new RuntimeException("argument too long!");
    }
    String result = "";
    for (int i = 0; i < str.length(); i++) {
      char c = str.charAt(i);
      if (ALLOWED.indexOf(c) == -1) {
        throw new RuntimeException(c + " Not allowed");
      }
      result += isUpperCase(c) ? toLowerCase(c) : toUpperCase(c);
    }
    return result;
  }
}

 JUnit5: dynamic test generation

// validating/tests/DynamicStringInverterTests.java
// (c)2017 MindView LLC: see Copyright.txt
// We make no guarantees that this code is fit for any purpose.
// Visit http://OnJava8.com for more book information.
package validating;

import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.DynamicTest.*;

import java.util.*;
import java.util.function.*;
import java.util.stream.*;
import org.junit.jupiter.api.*;

class DynamicStringInverterTests {
  // Combine operations to prevent code duplication:
  Stream<DynamicTest> testVersions(String id, Function<StringInverter, String> test) {
    List<StringInverter> versions =
        Arrays.asList(
            new Inverter1(), new Inverter2(),
            new Inverter3(), new Inverter4());
    return DynamicTest.stream(
        versions.iterator(),
        inverter -> inverter.getClass().getSimpleName(),
        inverter -> {
          System.out.println(inverter.getClass().getSimpleName() + ": " + id);
          try {
            if (test.apply(inverter) != "fail") {
              System.out.println("Success");
            }
          } catch (Exception | Error e) {
            System.out.println("Exception: " + e.getMessage());
          }
        });
  }

  String isEqual(String lval, String rval) {
    if (lval.equals(rval)) {
      return "success";
    }
    System.out.println("FAIL: " + lval + " != " + rval);
    return "fail";
  }

  @BeforeAll
  static void startMsg() {
    System.out.println(">>> Starting DynamicStringInverterTests <<<");
  }

  @AfterAll
  static void endMsg() {
    System.out.println(">>> Finished DynamicStringInverterTests <<<");
  }

  @TestFactory
  Stream<DynamicTest> basicInversion1() {
    String in = "Exit, Pursued by a Bear.";
    String out = "eXIT, pURSUED BY A bEAR.";
    return testVersions(
        "Basic inversion (should succeed)", inverter -> isEqual(inverter.invert(in), out));
  }

  @TestFactory
  Stream<DynamicTest> basicInversion2() {
    return testVersions(
        "Basic inversion (should fail)", inverter -> isEqual(inverter.invert("X"), "X"));
  }

  @TestFactory
  Stream<DynamicTest> disallowedCharacters() {
    String disallowed = ";-_()*&^%$#@!~`0123456789";
    return testVersions(
        "Disallowed characters",
        inverter -> {
          String result =
              disallowed
                  .chars()
                  .mapToObj(
                      c -> {
                        String cc = Character.toString((char) c);
                        try {
                          inverter.invert(cc);
                          return "";
                        } catch (RuntimeException e) {
                          return cc;
                        }
                      })
                  .collect(Collectors.joining(""));
          if (result.length() == 0) return "success";
          System.out.println("Bad characters: " + result);
          return "fail";
        });
  }

  @TestFactory
  Stream<DynamicTest> allowedCharacters() {
    String lowcase = "abcdefghijklmnopqrstuvwxyz ,.";
    String upcase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ ,.";
    return testVersions(
        "Allowed characters (should succeed)",
        inverter -> {
          assertEquals(inverter.invert(lowcase), upcase);
          assertEquals(inverter.invert(upcase), lowcase);
          return "success";
        });
  }

  @TestFactory
  Stream<DynamicTest> lengthNoGreaterThan30() {
    String str = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
    assertTrue(str.length() > 30);
    return testVersions(
        "Length must be less than 31 (throws exception)", inverter -> inverter.invert(str));
  }

  @TestFactory
  Stream<DynamicTest> lengthLessThan31() {
    String str = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
    assertTrue(str.length() < 31);
    return testVersions(
        "Length must be less than 31 (should succeed)", inverter -> inverter.invert(str));
  }
}

references:

1. On Java 8 - Bruce Eckel

2. https://github.com/wangbingfeng/OnJava8-Examples/blob/master/validating/StringInverter.java

3. https://github.com/wangbingfeng/OnJava8-Examples/blob/master/validating/tests/StringInverterTests.java

猜你喜欢

转载自blog.csdn.net/wangbingfengf98/article/details/88934439