vendredi 9 septembre 2016

Should one interleave tests?

While I am not entirely new to testing and unit-testing in particular I often hesitate in regards to stuff like the following:

Imagine the following implementation I want to test (yes, I know 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;
}

While this is a constructed example I hope the intent is clear.

Now if I write tests is it enough to cover the behaviour for each "fetch"-method, or should I also cross my tests? What I mean by that:

Do I only assert that my return value of readInfo SomeInfo has the respective parameter set or do I always check for both?

(I am sorry, it's difficult to explain in English) should I interleave my tests like:

// 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);
}

Should I always add asserts for both values, or only for one? In both cases I would write 4 tests, so I wonder, because I was told that a good way to handle assertions is to just have one per test, else you're doing something wrong.

Aucun commentaire:

Enregistrer un commentaire