我最近接受了采访,要求我制作传统的FizzBuzz解决方案:
输出1到100之间的数字列表.
- 对于3和5的所有倍数,该数字将替换为"FizzBuzz"
- 对于所有剩余的3的倍数,该数字将替换为"Fizz"
- 对于所有剩余的5的倍数,该数字将替换为"Buzz"
我的解决方案是用Java编写的,但这不是必需的.面试官很想看到一些TDD的证据,所以本着这种精神,我开始制作我自己的本土FizzBuzz单元测试:
public class FizzBuzzTest {
@Test
public void testReturnsAnArrayOfOneHundred() {
String[] result = FizzBuzz.getResultAsArray();
assertEquals(100, result.length);
}
@Test
public void testPrintsAStringRepresentationOfTheArray() {
String result = FizzBuzz.getResultAsString();
assertNotNull(result);
assertNotSame(0, result.length());
assertEquals("1, 2", result.substring(0, 4));
}
@Test
public void testMultiplesOfThreeAndFivePrintFizzBuzz() {
String[] result = FizzBuzz.getResultAsArray();
// Check all instances of "FizzBuzz" in array
for (int i = 1; i <= 100; i++) {
if ((i % 3) == 0 && (i % 5) == 0) {
assertEquals("FizzBuzz", result[i - 1]);
}
}
}
@Test
public void testMultiplesOfThreeOnlyPrintFizz() {
String[] result = FizzBuzz.getResultAsArray();
// Check all instances of "Fizz" in array
for (int i = 1; i <= 100; i++) {
if ((i % 3) == 0 && !((i % 5) == 0)) {
assertEquals("Fizz", result[i - 1]);
}
}
}
@Test
public void testMultiplesOfFiveOnlyPrintBuzz() {
String[] result = FizzBuzz.getResultAsArray();
// Check all instances of "Buzz" in array
for (int i = 1; i <= 100; i++) {
if ((i % 5) == 0 && !((i % 3) == 0)) {
assertEquals("Buzz", result[i - 1]);
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
我的结果变为:
public class FizzBuzz {
private static final int MIN_VALUE = 1;
private static final int MAX_VALUE = 100;
private static String[] generate() {
List<String> items = new ArrayList<String>();
for (int i = MIN_VALUE; i <= MAX_VALUE; i++) {
boolean multipleOfThree = ((i % 3) == 0);
boolean multipleOfFive = ((i % 5) == 0);
if (multipleOfThree && multipleOfFive) {
items.add("FizzBuzz");
}
else if (multipleOfThree) {
items.add("Fizz");
}
else if (multipleOfFive) {
items.add("Buzz");
}
else {
items.add(String.valueOf(i));
}
}
return items.toArray(new String[0]);
}
public static String[] getResultAsArray() {
return generate();
}
public static String getResultAsString() {
String[] result = generate();
String output = "";
if (result.length > 0) {
output = Arrays.toString(result);
// Strip out the brackets from the result
output = output.substring(1, output.length() - 1);
}
return output;
}
public static final void main(String[] args) {
System.out.println(getResultAsString());
}
}
Run Code Online (Sandbox Code Playgroud)
整个解决方案在一天晚上花了大约20分钟,包括在提交之前紧张地检查我的代码的时间比需要的时间长得多:)
回顾我最初提交的内容:早期我决定将我的"倍数"计算合并到generate()方法中以避免过度工程,我现在认为这是一个错误; 另外,单独的getResultAsArray/generate方法显然是OTT.getResultAsString也可以与main()方法合并,因为一个只是委托给另一个.
我对TDD仍然缺乏经验,我觉得这可能让我失望了.我正在寻找其他方法,我可能会改进这种方法,特别是在TDD实践方面?
基于下面非常有用的建议,我已经重新设计了我现在认为更"TDD友好"的答案:
变化:
将FizzBuzz逻辑与输出生成分开,使解决方案更具可扩展性
每次测试只需一个断言,以简化它们
在每种情况下仅测试最基本的逻辑单元
还验证了确认弦乐构建的最终测试
代码:
public class FizzBuzzTest {
@Test
public void testMultipleOfThreeAndFivePrintsFizzBuzz() {
assertEquals("FizzBuzz", FizzBuzz.getResult(15));
}
@Test
public void testMultipleOfThreeOnlyPrintsFizz() {
assertEquals("Fizz", FizzBuzz.getResult(93));
}
@Test
public void testMultipleOfFiveOnlyPrintsBuzz() {
assertEquals("Buzz", FizzBuzz.getResult(10));
}
@Test
public void testInputOfEightPrintsTheNumber() {
assertEquals("8", FizzBuzz.getResult(8));
}
@Test
public void testOutputOfProgramIsANonEmptyString() {
String out = FizzBuzz.buildOutput();
assertNotNull(out);
assertNotSame(0, out.length());
}
}
public class FizzBuzz {
private static final int MIN_VALUE = 1;
private static final int MAX_VALUE = 100;
public static String getResult(int input) {
boolean multipleOfThree = ((input % 3) == 0);
boolean multipleOfFive = ((input % 5) == 0);
if (multipleOfThree && multipleOfFive) {
return "FizzBuzz";
}
else if (multipleOfThree) {
return "Fizz";
}
else if (multipleOfFive) {
return "Buzz";
}
return String.valueOf(input);
}
public static String buildOutput() {
StringBuilder output = new StringBuilder();
for (int i = MIN_VALUE; i <= MAX_VALUE; i++) {
output.append(getResult(i));
if (i < MAX_VALUE) {
output.append(", ");
}
}
return output.toString();
}
public static final void main(String[] args) {
System.out.println(buildOutput());
}
}
Run Code Online (Sandbox Code Playgroud)
TDD与XP和敏捷哲学密切相关的原因是有道理的.它驱使我们使用可测试代码的小单元.因此,像TheSimplestThingWhichCouldPossiblyWork或单一责任原则这样的概念不属于测试驱动的方法.
在您的方案中显然没有发生这种情况.你注意的是数字数组,而不是FizzBuzz位(线索确实存在于问题中).
显然你处于完全人为的状态,并且很难伪造TDD.但我希望"真正的"TDD代码能够暴露翻译方法.这个:
@Test
public void testOtherNumber() {
String result = FizzBuzz.translateNumber(23);
assertEquals("23", result);
}
@Test
public void testMultipleOfThree() {
String result = FizzBuzz.translateNumber(3);
assertEquals("Fizz", result);
}
@Test
public void testMultipleOfFive() {
String result = FizzBuzz.translateNumber(25);
assertEquals("Buzz", result);
}
@Test
public void testMultipleOfFifteen() {
String result = FizzBuzz.translateNumber(45);
assertEquals("FizzBuzz", result);
}
Run Code Online (Sandbox Code Playgroud)
关键在于每个产生清晰的结果,并且很容易从失败的测试开始.
完成FizzBuzz位后,可以轻松完成数组操作.关键是要避免硬编码.最初我们可能不想要一个完整的实现:生成相对少量的元素就足够了,比如15.这样做的好处是可以产生更好的设计.毕竟,如果面试官回来说"实际上我想要一个121个元素的阵列",你需要改变多少代码?多少次测试?
TDD面临的挑战之一就是知道从哪里开始.Gojko Adzic写了一篇发人深思的文章,描述了一个实现Go游戏的Coding Dojo.
"有没有机会暴露我的翻译方法会因为以后封装的原因而对我不利?"
TDD中最激烈争论的话题之一.可能的答案是:
没有正确的答案,通常取决于具体要求或个人心血来潮.例如,虽然FizzBuzz本身很简单,但我们经常需要编写代码来获取数据,应用业务规则并返回验证结果.有时,规则需要应用于单个数据项,有时针对整个记录集,有时针对任何一个.
因此,暴露这两种方法的API不一定是错误的.当然,在面试的情况下,它让您有机会讨论API设计的细微差别,这是一个很好的对话话题.
归档时间: |
|
查看次数: |
4779 次 |
最近记录: |