For Unit testing on C++ projects, we use googletest, leveraging the robust framework that Google maintains and uses on a daily basis. Some advanced documentation is available here, detailing advanced assertions and exception handling!

To use C++ Unit tests, set the main file to be the file you want to load classes/methods from, and write some C++ code to test the functionality. This framework contains specific helper methods to assist in testing, and should cover almost every need you may have for unit tests.

Include/Using

Our unit tests currently allow you to specify the code that goes inside the body of a googletest TEST function, which has some limitations. One of these limitations is that include  and using declarations won't work exactly how you think. We've added a bit of a shortcut to using these, where if you have these declarations at the very top of your unit test code, they'll be pulled up to the top of the file that we generate for testing. This allows you to still use these declarations.

Asserts

Assert statements will be how you test your student's code. Keep in mind that only the fatal assert statements will count towards the student's grade for a test case. Googletest has their own helper assert statements, listed below (pulled from the link above):

General Asserts

Warning!
ASSERT_TRUE/ASSERT_FALSE can have weird behavior if the functions you're testing have return types but are missing a return statement! Check this issue on GitHub for an explanation.

Binary Comparison

String Comparison

Example

For a quick and simple example that should convey how these tests can be used, we've put together a very simple bank account class:
BankAccount.cpp

#include "BankAccount.h"
#include < iostream >

  BankAccount::BankAccount() {
    currentBalance = 0;
    numberOfTransactions = 0;
  }

BankAccount::BankAccount(float pCurrentBalance) {
  currentBalance = pCurrentBalance;
  numberOfTransactions = 0;
}

void BankAccount::deposit(float amount) {
  currentBalance += amount;
  numberOfTransactions++;
}

void BankAccount::withdraw(float amount) {
  if (currentBalance >= amount) {
    currentBalance -= amount;
  } else {
    std::cout << "ERROR: Cannot withdraw " << amount << " since current balance is " << currentBalance << std::endl;
  }
  numberOfTransactions++;
}

float BankAccount::getCurrentBalance() {
  return currentBalance;
}

int BankAccount::getNumberOfTransactions() {
  return numberOfTransactions;
}

BankAccount.h

#ifndef BANKACCOUNT_DEFINED
#define BANKACCOUNT_DEFINED 1

class BankAccount {
  public:
    // constructors
    BankAccount();
    virtual~BankAccount() {}
    BankAccount(float currentBalance);
   
    // transformer methods
    void deposit(float amount);
    void withdraw(float amount);
 
    // observer methods
    float getCurrentBalance();
    int getNumberOfTransactions();
  protected:
  private:
    float currentBalance;
    int numberOfTransactions;
};
#endif


We zipped both of those up and used it as the perfect code. Now that we have a BankAccount class, we can use it with C++ Unit tests to check that the logic works the way we want it to.

Some example test cases (main file is set to 'BankAccount.cpp'):

Testing Constructor:

BankAccount *acc = new BankAccount();
ASSERT_EQ(acc->getCurrentBalance(), 0);
ASSERT_EQ(acc->getNumberOfTransactions(), 0);

Testing Deposit:

BankAccount *acc = new BankAccount();
acc->deposit(100);
ASSERT_EQ(acc->getCurrentBalance(), 100);

Testing Withdraw:

BankAccount *acc = new BankAccount();
acc->withdraw(50);
ASSERT_EQ(acc->getCurrentBalance(), 0);

Testing Withdraw and Deposit:

BankAccount *acc = new BankAccount();
acc->deposit(100);
acc->withdraw(50);
ASSERT_EQ(acc->getNumberOfTransactions(), 2);
ASSERT_EQ(acc->getCurrentBalance(), 50);

Gmock support

Mimir C++ Unit tests also support the gmock library, allowing for more control over your asserts. This library will be automatically included in your C++ unit tests, allowing you access to a whole namespace of helpful assert methods, ranging from checking if an array is sorted, to checking side effects of other methods/functions.

Basic Example:

A basic example of something you can do with gmock that you couldn't easily do with just googletest (comparing arrays):

using testing::ElementsAreArray;
int array_0[] = {0, 1, 2, 3};
int array_1[] = {0, 1, 2, 3};
ASSERT_THAT(array_0, ElementsAreArray(array_1));

More Feedback:

Or, as an example for a more verbose unit test, you can use the ContainerEq assert to provide extra feedback in the output about what the differences in the containers are.

using testing::ContainerEq;
int array_0[] = {0,1,2,3};
int array_1[] = {0,1,2,4};
ASSERT_THAT(array_0, ContainerEq(array_1));

Which will return the following output, allowing you to give some verbose feedback on what exactly is wrong:

[==========] Running 1 test from 1 test case.
[----------] Global test environment set-up.
[----------] 1 test from MimirTest
[ RUN      ] MimirTest.cppUnitTest
./tests.cpp:19: Failure
Value of: array_0
Expected: equals { 0, 1, 2, 4 }
  Actual: { 0, 1, 2, 3 } (of type int [4]), which has these unexpected elements: 3,
and doesn't have these expected elements: 4
[  FAILED  ] MimirTest.cppUnitTest (0 ms)
[----------] 1 test from MimirTest (0 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test case ran. (0 ms total)
[  PASSED  ] 0 tests.
[  FAILED  ] 1 test, listed below:
[  FAILED  ] MimirTest.cppUnitTest

 1 FAILED TEST

Important!

Remember that you will need to reference any of the new asserts that you want with a using statement before actually using them.

Questions?:

For other listings of commands and asserts that are available, be sure to check out the gmock cheatsheet, or the gtest advanced usage page!

Compiler Flags

We do also provide a way to apply compiler flags to unit test case compilation. This can be accomplished by adding a magic comment at line 1 of your unit test case code that looks like the following:

/*CXX_FLAGS {flags}*/ 

 Where you replace {flags} with what compiler flags you want to pass.

For an example:

/*CXX_FLAGS -Wall -Werror*/
int x = 0;

The above would actually fail to validate the test case, as -Werror is applied to both the student's submission (or your solution code), as well as your unit test case code.

This magic comment works on all versions of C++. It must appear as the first line of your unit test for this to work. 

Did this answer your question?