Gtest on new keyword

Viewed 265

new keyword in C++ will throw an exception if insufficient memory but below code trying to return "NO_MEMORY" when new failed. This is bad because it will raise std::bad_alloc exception .

I am writing a unit test(gtest). How to create a scenario to catch this problem.

class base{


    public: base(){
        std::cout<<"base\n";
    }
};
 

std::string getInstance(base** obj){

    base *bObject = new base();
    *obj = bObject; //updated
     return (NULL == bObject) ? "NO_MEMORY" : "SUCCESS"; // here is problem if new fail it raise an exception. How to write unit test to catch this?
}

int main()
{
 
 base *base_obj;
 getInstance(&base_obj);
}
 
2 Answers

Have you looked into EXPECT_THROW?

If you cannot absolutely change your code, (which is required if you want to use gmock), you can globally overload the new operator as the other answer suggested.

However, you should do this carefully since this operator is used by other functions including the ones in google test.

One way to do this is to use a global variable that makes the new operator throw conditionally. Note that this is not the safest way, specially if your program is using multithreading

Below is one way of testing the scenario you described using this method and the global variable g_testing.

// https://stackoverflow.com/questions/70925635/gtest-on-new-keyword

#include "gtest/gtest.h"

// Global variable that indicates we are testing. In this case the global new
// operator throws.
bool g_testing = false;

// Overloading Global new operator
void *operator new(size_t sz) {
  void *m = malloc(sz);
  if (g_testing) {
    throw std::bad_alloc();
  }

  return m;
}

class base {
 public:
  base() { std::cout << "base\n"; }
};

std::string getInstance(base **obj) {
  base *bObject = new base();
  *obj = bObject;  // updated
  return (NULL == bObject)
             ? "NO_MEMORY"
             : "SUCCESS";  // here is problem if new fail it raise an exception.
  // How to write unit test to catch this?
}

TEST(Test_New, Failure) {
  base *base_obj;

  // Simple usage of EXPECT_THROW. This one should THROW.
  g_testing = true;
  EXPECT_THROW(getInstance(&base_obj), std::bad_alloc);
  g_testing = false;

  std::string result1;
  // You can put a block of code in it:
  g_testing = true;
  EXPECT_THROW({ result1 = getInstance(&base_obj); }, std::bad_alloc);
  g_testing = false;
  EXPECT_NE(result1, "SUCCESS");
}

TEST(Test_New, Success) {
  base *base_obj;

  std::string result2;
  // This one should NOT throw an exception.
  EXPECT_NO_THROW({ result2 = getInstance(&base_obj); });
  EXPECT_EQ(result2, "SUCCESS");
}

And here is your working example: https://godbolt.org/z/xffEoW9Kd

First I think you need to catch the exception otherwise your program will never reach the point of returning NO_MEMORY:

std::string getInstance(base **obj) {
    try {
        if (!obj)
            throw std::invalid_argument("");

        *obj = new base();
        return "SUCCESS";
    }
    catch (const std::bad_alloc& e) {
        return "NO_MEMORY";
    }
    catch (...) {
        return "UNDEFINED_ERROR";
    }
}

A quick and dirty way for testing this would be to make the constructor (or an overloaded new) to throw std::bad_alloc:

#ifdef UNIT_TESTING
// simulate there is no memory
base::base() { throw std::bad_alloc; }
#else
base::base() { }
#endif

But I guess the proper way would be to use something like mockcpp

Edit: Since your are using gtest you might prefer using Google Mock to mock base with a constructor throwing bad_alloc instead of the dirty substitution of base::base

Related