3

I have an integration test that needs to tweak some values on the database. This sort of tweak is not needed for the real application, only to setup the test scenario.

The dilemma:

Should I manipulate the DB directly from within the test project (by running a custom SQL query or by adding EF to the test project), or should I add fine-grained methods to the BL/Domain model/DAL that have no real purpose other than helping those integration tests? Is there a better way to achieve this?

3 Answers 3

3

This isn't a dilemma. There is no reason to contort yourself in order to avoid methods for testing.

Your test suite is a vital component of the code base - the fact that it isn't shipped to the customer is irrelevant. All best practices that are appropriate for business code are still appropriate for test code as well. Therefore, if the reasonable solution to accomplish something in a transparent, efficient and maintainable way is to create a method for it, then create that method. Whether or not that method ever runs in production should not be a factor in that decision.

1
  • I decided to go along this path, however I still think it's better to add a TestService / TestRepository classes (in the real project, not inside the tests project) instead of adding the methods to the actual classes that the application normally uses, so that the 'real' classes won't get messed up with dozens of fine grained methods needed only for testing. What do you think about this approach?
    – BornToCode
    Commented Dec 13, 2017 at 8:38
2

Generally, fixture setup through public API is preferable. If this is not the case, having some direct db access as testing backdoor is also acceptable. Alternatively, we also can use Prebuilt Fixture, there are lots of way to do it such as sql populating, data pump, etc. All of these fixture setups have the same challenge - fixture tear down. Automated Teardown may help for the case setting up and tearing down through API. For Prebuilt Fixture, maybe you should use some database flashback mechanism.

Another thought is, though in your question, you assume integration test should through database. However, it's not necessary always the case. Due to the difficulty of the setup and tear down, we should always try to humble it. If your code already has repository pattern, you can just implement an in-memory hash map for data store. The beauty of this is you can rely on Garbage-Collected Teardown. If this is not the case, try using in-memory database, then you also don't need to clean up the data created by your tests.

0

If the data is only required for a specific test, this is generally achieved via a transaction which is rolled back at the end of the test. C# has TransactionScope for this purpose. I daresay other languages have similar constructs.

The benefit of this approach is that you don't need to concern yourself with tear-down. You just set up the data (arrange), do the test (act) and check the test has worked (assert). This becomes super-important should the test be inadvertently run on a live database (yes, I've seen it happen!).

6
  • Yes, but do you add your code (which uses TransactionScope) to the test project or to the actual Model/BL?
    – BornToCode
    Commented Dec 12, 2017 at 13:28
  • That rather depends on your code. Usually it goes in the test body itself but you might just want to put it in the test setup and teardown methods (see here). Transactions should ideally cover as small an amount of code as possible so as a minimum it would need to go round your arrange, act assert block.
    – Robbie Dee
    Commented Dec 12, 2017 at 14:16
  • Make It easy. You might need to validate commits without rollbacks. For instance for validating constraints violations. Just load dummy data into the tests DB. The first test of the suite could load the data and the last one could remove It. I found to be unecessary to have code in production that is never going to be executed. All the code you need for tests (IMO) should be in the tests.
    – Laiv
    Commented Dec 12, 2017 at 21:35
  • @Laiv Firstly you should rarely (if ever) rely on test ordering. Secondly, that doesn't defend against writing to the wrong DB. And thirdly, if the suite fails before you get to the last test, your DB is in an inconsistent state (tests should be repeatable).
    – Robbie Dee
    Commented Dec 12, 2017 at 21:39
  • How do you load the data is implementation detail. You could use the bootstraping mechanisms of your framework for load/unload the data. No need to set any specific order in the execution of the tests. The DB can be restored anytime because the dummies are known up front. Finally, I would not expect anybody poiting to the worng DB during the Integration tests. Executing rollbacks can cause you to ignore errors that will only happen during the commit of the whole transaction. Overall with ORM with cache.
    – Laiv
    Commented Dec 12, 2017 at 21:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.