Is it O.K. to use global data for multiple tests in the TDD cycle within the Google Test framework?

StackOverflow https://stackoverflow.com/questions/21232945

Domanda

I have just started using Gtest, and I am writing tests that look like this:

TEST(testFamily, specificTest) {
     initData data; 
     ASSERT_TRUE(boolean_algorithm(data)); 
}

The problem is that my testing code gets unnecesarily bloated because I have a lot of tests that can use the same data, so the line:

 initData data; 

shouldn't be repeated for each test. I know that tests should be independent of each other and in that aspect, using something like a constant global

const initData data; 

brings questions like "Are you sure your tests won't modify the global test data?". Should i keep initializing the same test configuration for each test? Sometimes the initialized test data requires much more code than one line....

È stato utile?

Soluzione

It is ok, but I would create that object in the testing class, rather then in the global scope (like explained in Test Fixtures: Using the Same Data Configuration for Multiple Tests).

For example, like this :

initData CreateData() 
{
    initData d;
    // set values
    return d;
}

class testFamily : public testing::Test
{
public:
    testFamily() :
            data( CreateData()  )
    {
    }
    ~testFamily()
    {
    }

    initData data;
};

TEST_F( testFamily, specificTest )
{
    ASSERT_TRUE(boolean_algorithm(data)); 
}

Now, the data structure is calculated (or set) only in one place (CreateData() function).

Altri suggerimenti

Disclaimer: I haven't used googletest before! But saying that...

It's never a good idea to share modifiable data between unit tests. A unit test should independant of any other unit tests and it shouldn't matter which order you run the unit tests in. The risk that you run by sharing data is that as a side-effect (or consequence) of data that is modified a test passes (or fails). A unit test should also be very quick to run, if it takes too long the test will be discarded or never run (and so pretty pointless).

But now the question is, what if it's expensive to create the test data? If your test data is dependant on a resource outside of the unit test (database, network connection etc) what you are actually doing is integration testing and these can be slow to run (and brittle if the data is not mananged correctly by resetting it after every test run).

I don't have any issue with re-using static (non-modifiable) data in unit tests (and setting this up when the test suite loads) but re-using data that may have be modified in some way is a big no-no.

Another question that perhaps you should ask yourself is,could I look to Mock or Stub out the objects I need?

To reuse data between tests

If your tests are not modifying your test data and you want to avoid regenerating that multiple times, then the recommended method is to use static members in your test fixture:

  1. Declare the data to be reused as a static member variable in your fixture class
  2. Define and initialize the static variables outside the class as usual
  3. Add a static void SetUpTestSuite() method to the fixture class for setup and, optionally, a static void TearDownTestSuite() method for tear-down.

Example code from the documentation:

class FooTest : public testing::Test {
 protected:
  // Per-test-suite set-up.
  // Called before the first test in this test suite.
  // Can be omitted if not needed.
  static void SetUpTestSuite() {
    // Avoid reallocating static objects if called in subclasses of FooTest.
    if (shared_resource_ == nullptr) {
      shared_resource_ = new ...;
    }
  }

  // Per-test-suite tear-down.
  // Called after the last test in this test suite.
  // Can be omitted if not needed.
  static void TearDownTestSuite() {
    delete shared_resource_;
    shared_resource_ = nullptr;
  }

  // You can define per-test set-up logic as usual.
  void SetUp() override { ... }

  // You can define per-test tear-down logic as usual.
  void TearDown() override { ... }

  // Some expensive resource shared by all tests.
  static T* shared_resource_;
};

T* FooTest::shared_resource_ = nullptr;

TEST_F(FooTest, Test1) {
  ... you can refer to shared_resource_ here ...
}

TEST_F(FooTest, Test2) {
  ... you can refer to shared_resource_ here ...
}

To reuse code to generate the data

Follow BЈовић's answer or the docs on test fixtures.

Note that each test will use a separate instance of the fixture class!

I think your concern comes down to two things :

  • Worry that you're unnecessarily consuming memory, or
  • Worry about unnecessarily repeating code.

Keep in mind that the reason tests should be separate is because if need be, they should be able to be run independently. In that respect, it's okay to keep re-initializing the same context for each test.

If you're worried about test-code maintainability, one idea would be that if you have a lot of tests that have a very similar context, then all the initialization stuff can be put into a common function that is just invoked. That will have the added advantage of keeping the code of the individual tests clean.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top