Pregunta

This is (again) a question of methodology...

Suppose we are testing a service that returns Articles given ids, i.e. List<Article> getArticles(List<Integer> ids);. In addition, the corresponding location will be null if the id is invalid.

Firstly fill the database by the SQL:

INSERT INTO articles VALUES (2001, "aaa"), (2002, "bbbb"), (2003, "cc"), (2004, "ddddd");

Then test by the following. Which way shall I do it (see details in the comments below)?

void testGetArticle() {
    // NOTE: WHICH WAY?

    // way1: one existing id, one nonexisting id.
    var ids = Arrays.newArrayList(2002, 9999);

    // way2: many existing ids, many nonexisting ids.
    var ids = Arrays.newArrayList(1000, 1001, 2002, 2003, 2004, 9997, 9998, 9999);

    List<Article> a = xx.getArticle(ids);
    assertNull(a.get(0));    assertEquals(a.get(1), ...);  ...etc... // assert each of the results
}

Thanks for any suggestions!

¿Fue útil?

Solución

For every assertion, ask yourself: does this improve confidence in the code? If two of the assertions test the exact same code and the only difference is what the actual values are in the test then it is very unlikely that both of them add value which the other one doesn't.

Some corollaries to that:

  1. This is why red/green/refactor TDD can be so powerful: you're supposed to think of the smallest step forward, and write a test for each such step. If done properly that means every new test forces the implementation to change, and so every test adds value.
  2. Variable names can help to show how two similar values differ in testing. For example:

    void setup() {
        var existingId = idGenerator.next();
        var existingAddress = "foo";
        [insert existingId with existingAddress into DB]
        var nonExistingId = idGenerator.next();
    }
    
    void shouldReturnAddressForExistingId() {
        assertEqual(existingAddress, a.getAddress(existingId))
    }
    
    void shouldReturnNullForNonExistingId() {
        assertEqual(null, a.getAddress(nonExistingId))
    }
    
  3. The "should" pattern above is intentional. I've found it's much better than the default "test" pattern at making you think about what each test is meant to prove (the "why") rather than which piece of code is being tested (the "what").
  4. Additional setup and assertions take time. When writing unit tests that isn't too costly, but writing DB tests like you're doing this could easily balloon into a huge problem, as it often does, where the test suite take hours to run.
  5. Superfluous tests will reduce the confidence of anyone reading the tests later, because they will know that
    1. you did not use TDD and
    2. you were unable to write the fastest test to verify every branch of the code.

Otros consejos

You must embrace all the possible use cases, including "on bound" cases, that is the representative case.There is no a "representative" case, which misses even one possible use case.

Licenciado bajo: CC-BY-SA con atribución
scroll top