虽然我并不是特别擅长测试和单元测试,但我经常对以下内容犹豫不决:
想象一下我想测试的以下实现(是的,我知道TDD ftw;))
private Value0Service value0Service = new Value0Service();
private Value1Service value1Service = new Value1Service();
public SomeInfo readInfo(final Long id, final Value value0, final Value value1) {
final Value1 actualValue0 = fetchValue0IfNullElseReturnGiven(id, value0);
final Value2 actualValue1 = fetchValue1IfNullElseReturnGiven(value1);
return SomeInfo(actualValue0, actualValue1);
}
private Value fetchValue0IfNullElseReturnGiven(Long id, Value value0) {
if(value0== null) {
return value0Service.getById(id);
}
return value0;
}
private Value fetchValue1IfNullElseReturnGiven(Value value0, Value value1) {
if(value1 == null) {
return value1Service.getByValue0(value0);
}
return value1;
}
虽然这是一个构造的例子,但我希望意图很明确。
现在,如果我编写测试是否足以覆盖每个“fetch”方法的行为,还是我还要跨越我的测试? 我的意思是:
我是否只断言readInfo
SomeInfo
的返回值是否具有相应的参数集?还是我总是检查它们?
(对不起,很难用英语解释)我应该交错我的测试,如:
// ValueServices are to be treated as mocks from here, so imagine Value0Service value0ServiceMock = mock(Value0Service.class) in the tests!
@Test
public void ifBothValuesAreGivenThenSomeInfoIsFilledWithGivenForBoth() {
Value value0 = new Value();
Value value1 = new Value();
SomeInfo info = myTestObject.readInfo(VALID_ID, value0, value1);
assertThat(info.getValue0()).isEqualTo(value0);
assertThat(info.getValue1()).isEqualTo(value1);
}
@Test
public void ifValue0IsNullAndValue1IsNotThenSomeInfoIsFilledWithFetchedForValue0AndGivenForValue1() {
Value value1 = new Value();
Value fetchedValue0 = new Value();
doReturn(fetchedValue0).when(value0ServiceMock).getById(VALID_ID);
SomeInfo info = myTestObject.readInfo(VALID_ID, null, value1);
assertThat(info.getValue0()).isEqualTo(fetchedValue0);
assertThat(info.getValue1()).isEqualTo(value1);
}
@Test
public void ifValue0IsNotNullAndValue1IsThenSomeInfoIsFilledWithGivenForValue0AndFetchedForValue1() {
Value value0 = new Value();
Value fetchedValue1 = new Value();
doReturn(fetchedValue1).when(value1ServiceMock).getByValue(value0);
SomeInfo info = myTestObject.readInfo(VALID_ID, value0, null);
assertThat(info.getValue0()).isEqualTo(value0);
assertThat(info.getValue1()).isEqualTo(fetchedValue1);
}
@Test
public void ifValue0IsNullAndValue1IsNullThenSomeInfoIsFilledWithFetchedForBoth() {
Value fetchedValue0 = new Value();
Value fetchedValue1 = new Value();
doReturn(fetchedValue0).when(value0ServiceMock).getById(VALID_ID);
doReturn(fetchedValue1).when(value1ServiceMock).getByValue(fetchedValue0);
SomeInfo info = myTestObject.readInfo(VALID_ID, null, null);
assertThat(info.getValue0()).isEqualTo(fetchedValue0);
assertThat(info.getValue1()).isEqualTo(fetchedValue1);
}
我是否应该始终为两个值添加断言,或仅为一个值添加断言?在这两种情况下,我都会编写4个测试,所以我想知道,因为我被告知处理断言的好方法是每个测试只有一个,否则你做错了。
答案 0 :(得分:1)
在此特定示例中,您应该为SomeInfo
实现equals方法。这将允许您只有一个断言:
SomeInfo expectedInfo = new SomeInfo(...);
assertThat(myTestObject.readInfo(VALID_ID, null, null)).isEqualTo(expectedInfo);
然而,你的问题仍然存在,并且是我一直反对的问题。每次测试只有一个断言对于确保未来的程序员可以读取测试至关重要。诀窍是决定什么算作“一个断言”。让我们来看看你的例子:
assertThat(info.getValue0()).isEqualTo(fetchedValue0);
assertThat(info.getValue1()).isEqualTo(fetchedValue1);
你可能会争辩说,尽管对assertThat
方法有两次调用,但这只是一个逻辑断言 - 我们正在检查info
的值是否正确。当你有两个这样的断言时会出现问题:
@Test
public void multipleAssertions() {
Value value1 = serviceUnderTest.getValue(...);
assertThat(value1).isEqualTo(expectedValue1);
Value value2 = serviceUnderTest.getValue(...);
assertThat(value2).isEqualTo(expectedValue2);
}
这种代码使测试变得混乱且难以维护。你经常会发现这样的测试也没有很好的命名。
最后一点:在编写代码之后编写测试时,只会出现此问题。正如你所说,“TDD ftw!”。如果首先编写测试,然后使用尽可能少的代码来进行测试,那么您永远不会遇到此问题,因为您只是在第一次断言后停止编写测试并继续进行下一次测试。