0

I have recently developed a small module that performs queries against a database. At the end of each test that contains code performing modifying queries against the database, I added an assert that checks that a record irrelevant to the query did not get affected. My reasoning is that I want to prevent a future developer from, for example, omitting the where clause in an update query and causing catastrophic side effects which won't be caught in tests. I created an "irrelevant record" in my database setup and a function that asserts the record hasn't changed. I call this function at the end of each test.

I have several things I am not comfortable with in this solution:

  1. Each test case doesn't only test its described test case, but also the unwanted side effect case. The alternative is duplicating each test twice which I think will pollute the test suite and make it hard to maintain and add new tests. I feel this is wrong as a methodology, as for example if in the future I'll want to do n wide assertions on each test case, this will entail making n copies of each test, which sounds horrible.
  2. I have the feeling that testing this is overkill, but on the other hand without such an assertion there is not guarantee of unwanted side effects not happening.

My question is how to approach this kind of problem, specifically checking for unwanted side effects in a module that has database access involved, but more generally when wanting to test a common assertion for all test cases.

Thanks.

2
  • How likely is it that a reviewer will miss such a mistake? How likely is it that for a new testcase, the extra check is skipped/forgotten? Commented Jan 13, 2023 at 10:05
  • 1
    #1 is quite biased and lacks perspective. You are forcing everyone to read every single test to know "what should not happen given X when Y then Z". However, tests suits intendedly focused on test cases asserting those "unwanted side effects" makes a good deal of improvement. Tests are not mere automatisms. They are documentation too. Plus, where do you draw the line between "wanted" and "unwanted"? According to your rationale, you should assert the state of the whole application after the test execution. That's fairly too much and beyond any test responsibility. Commented Jan 13, 2023 at 14:25

2 Answers 2

4

A module that operates using a language as powerful as database query languages certainly warrants extreme paranoia while testing. And it is far worse to under-cover than to over-cover functionality.

The trade-off you must consider is whether redundant tests like this affect the readability and the execution speed of your test suite. If you can find a a way to automatically test general sanity constraints after each test, whether through inheritance, decorators, or other means, and they don't make the test suite unusably slow, then such repeated checks are a very good idea.

0

If you are following the single responsibility pattern, your test subject should have a natural scope to it which will limit the things that can probably go wrong.

I think its fine to have more than one assert per test. Your test names should mirror the requirements as stated by the specification rather than all the possible failures that might happen. so..

UpdateOrder_ChangesTheOrderToANewValue() { var t = new Repo(); t.AddOrder(order1) t.AddOrder(order2) order1.name = "changed" t.UpdateOrder(order1) var expected1 = t.GetOrder(1); var expected2 = t.GetOrder(2); Assert(expect1 != null, "original order was not found") Assert(expect1 = order1, "order did not update!") Assert(expect2 = order2, "update changed other orders!!") Assert.WorldNotExploded(); //etc } 

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.