user5580578
user5580578

Reputation: 1334

unit testing: static create method

I want to start writing some unit tests for a buffer class and I`m struggling a bit with the static create method.

class MyBuffer
{
public: 
  static MyBuffer* create(int32 bufSize)
  {
     if(!bufSize)
       return nullptr; 

     char* internalBuf = (char*)malloc(bufSize);
     if(nullptr == internalBuf) return nullptr; 

     return new MyBuffer(internalBuf, bufSize);
  }

 public bool write(void* data, int32 dataSize) {...}

private:
 MyBuffer(char* buf, int32 size) : internalBuf(buf), size(bufSize) {}

 char* internalBuf = nullptr; 
 int32 bufSize = 0;
};

//just dummy code
TEST(MyBuffer bufferWithZeroSize)
{ 
   auto buf = MyBuffer::create(0);
   ASSERT_True(nullptr == buf);
}

TEST(MyBuffer writeDataToBuffer)
{
  //arrange
  auto buf = MyBuffer::create(50); 
  ASSERT_NotNull(buf); //is this call needed

  //act
  buf->write(...); 

  //assert 
}

Verifying the MyBuffer creation process seems to be ok.

In my 2nd test, is it ok to assume that the creation process of the MyBuffer was successfull? Otherwise I have to check it in the "act" part of the unit test. This sounds not so reasonable to me because I want to check only one thing per unit test. On the other side, I know the malloc call can fail.

How do you test objects with a static create method? Are there any useful test strategies or is it recommend to refactor such objects? I mainly use the static create methods in order to avoid that the caller of the object forget to initialize the object correctly. Therefore it sounds to me a valid approach.

Upvotes: 0

Views: 64

Answers (2)

Swordfish
Swordfish

Reputation: 13134

I mainly use the static create methods in order to avoid that the caller of the object forget to initialize the object correctly.

There is no way the user could possiby "forget" to initialize an instance of the MyBuffer shown below.

#include <cstddef>

class MyBuffer
{
private:
    std::size_t bufSize;
    char* internalBuf;

public:
    MyBuffer(std::size_t size)
    : bufSize{ size }
      internalBuf{ new char[size] },
    {}

    // + whats missing to satisfy the rule of 5

    bool write(char *data, std::size_t dataSize)
    {
        /* ... */
        return true;
    }
};

Notice: Don't use malloc() for no appearant reason. Use std::size_t for the size of objects in memory.

Update regarding latest comment

#include <cstddef>
#include <stdexcept>
#include <algorithm>

class MyBuffer
{
public:
    static constexpr std::size_t min_size{ 10 };
    static constexpr std::size_t max_size{ 42 };

private:
    std::size_t bufSize;
    char* internalBuf;

public:
    MyBuffer(std::size_t size)
    : bufSize{ size == std::clamp(size, min_size, max_size) ?
               size : throw std::invalid_argument{ "size is out of range!" } },
      internalBuf{ new char[bufSize] }
    {}
};

Upvotes: 1

darune
darune

Reputation: 11000

I suspect you don't need to write a buffer class in the first place and that std::vector<char> can be used instead. In general you should try to steer away from raw memory management (don't write new/delete/malloc/free at all (especially the delete/free)).

As for the unit testing question, yes, it is fine to some setup in order to arrive at your actual test(or tests). When you reuse setup across tests its often called a Fixture. I think it is not uncommon to test several things within the same test. Sometimes performance of the tests themselves can also be a thing, and then it makes sense to test more things if the setup is costly.

Upvotes: 0

Related Questions