3

Currently a sql unit test consists of 5 sections:

- Pre-Initialise
- Pre-Test
- Test
- Post-Test
- Post-Cleanup

For some of my tests the "pre-test" section is the same, and I have to copy-paste the Pre-Test code multiple times, code duplication is a bad idea... So my question is if there any way to write the pre-test somewhere only once and run it by calling a function within the unit-test Project?

Example of my current C# file behind one my my testcases:

[TestClass()]
public class SqlServerUnitTestAssignUserToAssignment : SqlDatabaseTestClass
{
  private TransactionScope trans;

  public SqlServerUnitTestAssignUserToAssignment()
  {
    InitializeComponent();
  }

  [TestInitialize()]
  public void TestInitialize()
  {
    trans = new TransactionScope();
    base.InitializeTest();
  }
  [TestCleanup()]
  public void TestCleanup()
  {
    base.CleanupTest();
    trans.Dispose();
  }

  // generated code below:

  Designer support code

  Additional test attributes

  [TestMethod()]
  public void core_AssignUserToAssignmentTest()...

  private Sql DatabaseTestActions core_AssignUserToAssignmentTestData
}
NomenNescio
  • 2,899
  • 8
  • 44
  • 82

2 Answers2

5

The two pieces to the puzzle are:

  • Per class "Common Scripts": If you need common code to run before or after all tests within a given test class, just put SQL into the Common Scripts for that class using the Designer.

  • Base class test initializers: If you need common code to run before or after all tests within a given project (or solution), create a new base class like so and make each test class derive from it.

-

public class MySqlTestsBase : SqlDatabaseTestClass
{
    [TestInitialize]
    public void TestInitialize()
    {
        PrivilegedContext = TestService.OpenPrivilegedContext();

        CleanupTables();

        //"Common Scripts" are run
        base.InitializeTest(); 
    }

    [TestCleanup]
    public void TestCleanup()
    {
        CleanupTables();

        base.CleanupTest();
    }

    /// <summary>
    /// Deletes data from all tables that tests might have affected.
    /// </summary>
    private void CleanupTables()
    {
        TestService.Execute(PrivilegedContext, PrivilegedContext, new SqlDatabaseTestAction
        {
            SqlScript = @"
                DELETE CustomerOrder
                DELETE Customer
                DELETE Order
            "
        });
    }
}
Chad
  • 3,159
  • 4
  • 33
  • 43
  • Good answer...child class should also get rid of TestInitialize & TestCleanup methods if derived from this base class – Mayank Jha Jul 25 '17 at 09:37
0

This is a great example of how you can listen to your tests, and let them influence the design of your system. (So I hope I can do it justice.)

You said:

So my question is if there any way to write the pre-test somewhere only once and run it by calling a function within the unit-test Project?

Imagine that you were able to consolidate this pre-test code into a helper function, which you could then invoke from within the test project. This solves the problem for your tests... but what about other consumers of your code? Wouldn't they benefit from this consolidation, as well?

It is helpful to think of our tests as regular consumers of our code. In this post about TDD, "Uncle Bob" Martin says (emphasis mine):

Have you ever integrated a third party library into your project? You got a big manual full of nice documentation. At the end there was a thin appendix of examples. Which of the two did you read? The examples of course! That's what the unit tests are! They are the most useful part of the documentation. They are the living examples of how to use the code.

If your tests are telling you that they need boilerplate code to get started, they are telling you that everyone needs this boilerplate code.

After you refactor this duplicate code into a formal class (or classes), these tests can use that code to perform the necessary initialization/cleanup - just like what you were originally aiming for. But now the rest of the system benefits from this effort, as well.

Community
  • 1
  • 1
Lilshieste
  • 2,744
  • 14
  • 20
  • 3
    While you make good points, you don't provide any examples or how to actually make this work within the VS SQL Unit Test framework. Of course we want to reuse code, but the person posting the question likely meant they didn't know how to do that and needed help with this specific framework. – Chad Jul 18 '14 at 22:49
  • @Chad Thanks for the feedback. I didn't include examples on how to make this work within the framework because doing so would have been counter to the spirit of my answer. I know testing frameworks are meant to offload tedious tasks from our tests, but sometimes this offloading hides shortcomings in our API design. My point was that when this type of situation occurs, it should be taken as an opportunity to gather new perspectives on the usage of our API, and make adjustments accordingly. (IME, this is more often the case for "pre-test" code than "cleanup", which is why I chose to address it.) – Lilshieste Jul 21 '14 at 04:23