开发者

Testing a class that depends on static functions of another class

开发者 https://www.devze.com 2023-01-23 05:37 出处:网络
I am currently working on a class that uses another class which has only static functions. Everything worked fine until I tried testing my class.

I am currently working on a class that uses another class which has only static functions.

Everything worked fine until I tried testing my class.

Here is a simple code example of the problem:

class A {
    static String getSometing() {
        return String("Something: ") + heavyCalculation().asString();
    }
}

class B {
    B() {}
    ~B() {}
    String runSomething(const String& name) {
        if(name.equals("something")) {
            return A::getSomething();
        } else {
            return "Invalid name!";
        }
    }
}

Assuming class A is working correctly (and has been tested by its unit tests), I would like to check the runSomething function in class B.

My first option would be creating mocks for the inner classes (In this sample - class A), but in that case it will give me nothing to inherit from A because it has only static functions.

My second option is to encapsulate the calls for A class in private functions 开发者_如何转开发inside B so I could control their return values (although choosing this option will make the good a little bit more complex).

My question to you is: Are there in better ways to test C++ classes that depends on static classes/functions than my current options?

Thanks in advance,

Tal.


I have had success in unit testing in similar situations by refactoring out the references to the static functions (in my case it was external dependencies) into new private functions and overwriting them on the tested stub, so I can recommend this approach

If the refactored functions remain private, they should not impact greatly on the complexity of the design and they should be small enough not to negatively impact on the readability of the code.


If you're not using a monolithic test suite, then its easy. I Assume that you have class A in A.cpp and class B in B.cpp, and the tests for B are in B_test.cpp.

Create a file called A_mock.cpp

class A
{
    static String getSometing() {
        return String("Expected Something");
    }
};

Then when compiling your B_test file, just link with A_mock.o rather than A.o .

g++ -Wall B_test.cpp B.cpp A_mock.cpp


You could pass a pointer to the function to the constructor of the class A. Then for testing, you can pass some the pointer to a mock function where you can do whatever you want.


Why the static function? I would suggest not making it static.

You could then create an interface for class A (in C++ this means a class with only pure virtual function headers) named AInterface. Class A would implement (inherit) AInterface and implement this virtual functions.

Then pass a pointer to this interface into class B's constructor and store it in a member variable named m_A. Then in your test, create MockClassA that implements AInterface. Pass MockClassA into class B constructor and set m_A to the input.

class AInterface
{
   virtual String getSomething() = 0;
}

class A : public AInterface
{
    String getSometing() {
        return String("Something: ") + heavyCalculation().asString();
    }
}

class B 
{
    B(AInterface A) :  { m_A = A; }
    ~B() {}
    String runSomething(const String& name) {
        if(name.equals("something")) {
            return m_A.getSomething();
        } else {
            return "Invalid name!";
        }
    }
    AInterface m_A;
}

Test code

class MockClassA : public AInterface
{
    String getSometing() {
        return String("Whatever I want. This is a test");
    }
}   

void test ()
{
   // "MockClassA" would just be "A" in regular code
   auto instanceOfB = B(MockClassA());

   String returnValue = instanceOfB.runSomething("something");
   :
   :
}


You should take the class via template, and explicitly export that instantiation (B<A>) to avoid linker issues if it was not previously all inline as posted. This way, you can insert other classes for the purposes of testing as you require and is good practice anyway. I'm also curious as to why your example looks so much like Java - I had to read it about five times before determining that it actually was C++ as stated.

template<typename T> class BImpl {
    String runSomething(const String& name) {
        if(name.equals("something")) {
            return T::getSomething();
        } else {
            return "Invalid name!";
        }
    }
};
typedef BImpl<A> B; // Just plugs in to existing code.

Now you can substitute a mock class for A, even though you can't inherit from it. Infact, this is ALSO extendable in another way - CRTP.

class A : public BImpl<A> {
    String getSomething() {
        // Now it's non-static! IT'S A MIRACLE!
    }
}

The wonders of templates never, ever cease to amaze me.


I would say, "Man, some people take unit testing way too far!"

Just test the two classes as a single unit. Class A is hard coded into class B anyway.

0

精彩评论

暂无评论...
验证码 换一张
取 消