473 lines
15 KiB
Markdown
473 lines
15 KiB
Markdown
## GTEST
|
|
|
|
* <!-- .element: class="fragment fade-in" --> <code>TEST(...)</code> Create single test
|
|
* <!-- .element: class="fragment fade-in" --> <code>TEST_F(...)</code> Use fixtures for testing
|
|
* <!-- .element: class="fragment fade-in" --> <code>TEST_P(...)</code> Use package of parameters for all tests
|
|
* <!-- .element: class="fragment fade-in" --> <code>EXPECT_*</code> Check condition
|
|
* <!-- .element: class="fragment fade-in" --> <code>ASSERT_*</code> Check condition and stop test if fails.
|
|
* <!-- .element: class="fragment fade-in" --> <code>EXPECT_PRED*</code> Allow to create own predicate, and other format of display errors
|
|
* <!-- .element: class="fragment fade-in" --> <code>testing::FloatLE</code> and <code>testing::DoubleLE</code> can be use for <code>EXPECT_PRED_FORMAT*</code> for floating-point comparison
|
|
* <!-- .element: class="fragment fade-in" --> <code>GTEST_SKIP()</code> can be use for skipping the test
|
|
* <!-- .element: class="fragment fade-in" --> <code>EXPECT_EXIT</code> can test signall which end program, like: <code>SIGKILL</code>
|
|
* <!-- .element: class="fragment fade-in" --> <code>EXPECT_THROW</code> will catch and check exceptions
|
|
|
|
___
|
|
|
|
## TEST
|
|
|
|
To create a simple test we can use `TEST` macro
|
|
|
|
```C++
|
|
class Calculator {
|
|
public:
|
|
int add(int lsh, int rhs) { return lhs + rhs; }
|
|
};
|
|
|
|
TEST(CalculatorTest, ShouldAddTwoValues) {
|
|
Calculator calc;
|
|
EXPECT_EQ(calc.add(10, 20), 10 + 20);
|
|
}
|
|
```
|
|
|
|
<!-- .slide: data-background="#ccc" -->
|
|
___
|
|
|
|
## TEST_F
|
|
|
|
If we need to set up some structures before creating a test, we shouldn't do this every test, but instead create a test fixture that sets everything up during construction. It also provides useful methods which we can use in every test. This fixture is created first, then a test starts.
|
|
|
|
```C++
|
|
class DownloaderTest : public testing::Test {
|
|
public:
|
|
DownloaderTest() {
|
|
_connection = std::make_unique<TcpConnection>();
|
|
_connection->set_certificates(_certificateMock);
|
|
_downloader = std::make_unqiue<UrlDownloader>(_connection.get());
|
|
}
|
|
|
|
CertificateMock _certificateMock;
|
|
std::unqiue_ptr<Connection> _connection;
|
|
std::unique_ptr<Downloader> _downloader;
|
|
};
|
|
|
|
TEST_F(DownloaderTest, shouldDownloadImage) {
|
|
EXPECT_CALL(_certificateMock, validate).WillOnce(Return(true));
|
|
EXPECT_TRUE(_downloader.downloadImage("exampleImg"));
|
|
}
|
|
```
|
|
<!-- .slide: style="font-size: 0.77em" -->
|
|
___
|
|
|
|
## TEST_P (1)
|
|
|
|
Whenever we need to run the same test but with a different set of parameters we should use `TEST_P`
|
|
|
|
```C++
|
|
class Firewall {
|
|
public:
|
|
virtual ~Firewall() = default;
|
|
|
|
virtual bool block(const std::string& ipAddress) = 0;
|
|
};
|
|
|
|
class WhiteListFirewall : public Firewall {
|
|
public:
|
|
void addToWhitelist(const std::string& ipAddress) {
|
|
whiteList_.push_back(ipAddress);
|
|
}
|
|
|
|
bool block(const std::string& ipAddress) override {
|
|
return std::find(cbegin(whiteList_), cend(whiteList_), ipAddress) == std::cend(whiteList_);
|
|
}
|
|
|
|
private:
|
|
std::vector<std::string> whiteList_;
|
|
};
|
|
|
|
```
|
|
<!-- .element: class="fragment fade-in" -->
|
|
<!-- .slide: style="font-size: 0.77em" -->
|
|
___
|
|
<!-- .slide: data-background="#ccc" -->
|
|
## TEST_P (2)
|
|
|
|
```C++
|
|
class TestFirewall : public testing::TestWithParam<std::pair<std::string, bool>> {
|
|
};
|
|
|
|
TEST_P(TestFirewall, ShouldFilterAdresses) {
|
|
auto firewall = std::make_unique<WhiteListFirewall>();
|
|
firewall->addToWhitelist("192.201.1.12");
|
|
firewall->addToWhitelist("192.122.1.12");
|
|
|
|
const auto& [address, isBlock] = GetParam();
|
|
EXPECT_EQ(firewall->block(address), isBlock);
|
|
}
|
|
|
|
INSTANTIATE_TEST_SUITE_P(TestFirewallParameters,
|
|
TestFirewall,
|
|
testing::Values(
|
|
std::pair<std::string, bool>{"192.178.1.12", true},
|
|
std::pair<std::string, bool>{"192.144.1.12", true},
|
|
std::pair<std::string, bool>{"192.188.1.12", true},
|
|
std::pair<std::string, bool>{"192.201.1.12", false},
|
|
std::pair<std::string, bool>{"192.122.1.12", false}));
|
|
```
|
|
<!-- .element: class="fragment fade-in" -->
|
|
<!-- .slide: style="font-size: 0.8em" -->
|
|
___
|
|
<!-- .slide: data-background="#ccc" -->
|
|
## TEST_P (3)
|
|
|
|
```
|
|
[----------] 5 tests from TestFirewallParameters/TestFirewall
|
|
[ RUN ] TestFirewallParameters/TestFirewall.ShouldFilterAdresses/0
|
|
[ OK ] TestFirewallParameters/TestFirewall.ShouldFilterAdresses/0 (0 ms)
|
|
[ RUN ] TestFirewallParameters/TestFirewall.ShouldFilterAdresses/1
|
|
[ OK ] TestFirewallParameters/TestFirewall.ShouldFilterAdresses/1 (0 ms)
|
|
[ RUN ] TestFirewallParameters/TestFirewall.ShouldFilterAdresses/2
|
|
[ OK ] TestFirewallParameters/TestFirewall.ShouldFilterAdresses/2 (0 ms)
|
|
[ RUN ] TestFirewallParameters/TestFirewall.ShouldFilterAdresses/3
|
|
[ OK ] TestFirewallParameters/TestFirewall.ShouldFilterAdresses/3 (0 ms)
|
|
[ RUN ] TestFirewallParameters/TestFirewall.ShouldFilterAdresses/4
|
|
[ OK ] TestFirewallParameters/TestFirewall.ShouldFilterAdresses/4 (0 ms)
|
|
[----------] 5 tests from TestFirewallParameters/TestFirewall (0 ms total)
|
|
```
|
|
<!-- .element: class="fragment fade-in" -->
|
|
<!-- .slide: style="font-size: 0.8em" -->
|
|
___
|
|
<!-- .slide: data-background="#ccc" -->
|
|
## TEST_P - inherit from other test
|
|
|
|
```C++
|
|
class BlackListFirewall : public Firewall {
|
|
public:
|
|
void addToBlacklist(const std::string& ipAddress) {
|
|
blackList_.push_back(ipAddress);
|
|
}
|
|
|
|
bool block(const std::string& ipAddress) override {
|
|
return std::find(cbegin(blackList_), cend(blackList_), ipAddress) != std::cend(blackList_);
|
|
}
|
|
|
|
private:
|
|
std::vector<std::string> blackList_;
|
|
};
|
|
|
|
class BaseTestBlackListFirewall : public testing::Test {
|
|
public:
|
|
void SetUp() override {
|
|
firewall_ = std::make_unique<BlackListFirewall>();
|
|
|
|
firewall_->addToBlacklist("192.201.1.12");
|
|
firewall_->addToBlacklist("192.122.1.12");
|
|
firewall_->addToBlacklist("192.178.1.12");
|
|
firewall_->addToBlacklist("192.144.1.12");
|
|
firewall_->addToBlacklist("192.188.1.12");
|
|
}
|
|
|
|
BlackListFirewall* firewall() const { return firewall_.get(); }
|
|
|
|
private:
|
|
std::unique_ptr<BlackListFirewall> firewall_;
|
|
};
|
|
```
|
|
<!-- .element: class="fragment fade-in" -->
|
|
<!-- .slide: style="font-size: 0.62em" -->
|
|
___
|
|
<!-- .slide: data-background="#ccc" -->
|
|
## testing::WithParamInterface
|
|
|
|
```C++
|
|
class TestBlackListFirewall : public BaseTestBlackListFirewall,
|
|
public testing::WithParamInterface<std::string> {
|
|
};
|
|
|
|
TEST_P(TestBlackListFirewall, ShouldBlock) {
|
|
EXPECT_TRUE(firewall()->block(GetParam()));
|
|
}
|
|
|
|
INSTANTIATE_TEST_SUITE_P(TestBlackListFirewallPackage,
|
|
TestBlackListFirewall,
|
|
testing::Values(
|
|
"192.178.1.12",
|
|
"192.144.1.12",
|
|
"192.188.1.12",
|
|
"192.201.1.12",
|
|
"192.122.1.12"));
|
|
```
|
|
<!-- .element: class="fragment fade-in" -->
|
|
|
|
```
|
|
[----------] 5 tests from TestBlackListFirewallPackage/TestBlackListFirewall
|
|
[ RUN ] TestBlackListFirewallPackage/TestBlackListFirewall.ShouldBlock/0
|
|
[ OK ] TestBlackListFirewallPackage/TestBlackListFirewall.ShouldBlock/0 (0 ms)
|
|
[ RUN ] TestBlackListFirewallPackage/TestBlackListFirewall.ShouldBlock/1
|
|
[ OK ] TestBlackListFirewallPackage/TestBlackListFirewall.ShouldBlock/1 (0 ms)
|
|
[ RUN ] TestBlackListFirewallPackage/TestBlackListFirewall.ShouldBlock/2
|
|
[ OK ] TestBlackListFirewallPackage/TestBlackListFirewall.ShouldBlock/2 (0 ms)
|
|
[ RUN ] TestBlackListFirewallPackage/TestBlackListFirewall.ShouldBlock/3
|
|
[ OK ] TestBlackListFirewallPackage/TestBlackListFirewall.ShouldBlock/3 (0 ms)
|
|
[ RUN ] TestBlackListFirewallPackage/TestBlackListFirewall.ShouldBlock/4
|
|
[ OK ] TestBlackListFirewallPackage/TestBlackListFirewall.ShouldBlock/4 (0 ms)
|
|
[----------] 5 tests from TestBlackListFirewallPackage/TestBlackListFirewall (0 ms total)
|
|
```
|
|
<!-- .element: class="fragment fade-in" -->
|
|
<!-- .slide: style="font-size: 0.7em" -->
|
|
___
|
|
|
|
<!-- .slide: data-background="#ccc" -->
|
|
## EXPECT_PRED* - own error message
|
|
|
|
```C++
|
|
testing::AssertionResult isBlocked(Firewall* firewall, const std::string& addr, bool block) {
|
|
if (firewall->block(addr) == block) {
|
|
return testing::AssertionSuccess();
|
|
}
|
|
|
|
return testing::AssertionFailure() << addr << (block ? " should be blocked" : " shouldn't be blocked");
|
|
}
|
|
|
|
TEST_F(BaseTestBlackListFirewall, ShouldFilter) {
|
|
EXPECT_TRUE(isBlocked(firewall(), "192.188.1.12", true));
|
|
EXPECT_TRUE(isBlocked(firewall(), "192.188.1.12", false));
|
|
}
|
|
```
|
|
<!-- .element: class="fragment fade-in" -->
|
|
|
|
```
|
|
[----------] 1 test from BaseTestBlackListFirewall
|
|
[ RUN ] BaseTestBlackListFirewall.ShouldFilter
|
|
/home/mateusz.adamski/Documents/repoGit/advanced_cpp/examples/gtest.cpp:101: Failure
|
|
Value of: isBlocked(firewall(), "192.188.1.12", false)
|
|
Actual: false (192.188.1.12 shouldn't be blocked)
|
|
Expected: true
|
|
[ FAILED ] BaseTestBlackListFirewall.ShouldFilter (0 ms)
|
|
[----------] 1 test from BaseTestBlackListFirewall (0 ms total)
|
|
```
|
|
<!-- .element: class="fragment fade-in" -->
|
|
<!-- .slide: style="font-size: 0.7em" -->
|
|
___
|
|
<!-- .slide: data-background="#ccc" -->
|
|
## EXPECT_PRED* - check string
|
|
|
|
```C++
|
|
for (const auto& str : {"There is a nauka!", "nauka.programowania.ma@gmail.com"}) {
|
|
EXPECT_THAT(str, testing::MatchesRegex(
|
|
"^([a-zA-Z0-9_\\-\\.]+)@([a-zA-Z0-9_\\-\\.]+)\\.([a-zA-Z]{2,5})$"));
|
|
|
|
ASSERT_THAT(str, testing::HasSubstr("nauka"));
|
|
}
|
|
```
|
|
<!-- .element: class="fragment fade-in" -->
|
|
|
|
```
|
|
[ RUN ] ExampleTest.ShouldFail
|
|
/home/mateusz.adamski/Documents/repoGit/advanced_cpp/examples/gtest.cpp:104: Failure
|
|
Value of: str
|
|
Expected: matches regular expression "^([a-zA-Z0-9_\\-\\.]+)@([a-zA-Z0-9_\\-\\.]+)\\.([a-zA-Z]{2,5})$"
|
|
Actual: 0x5625cd2c080c pointing to "There is a nauka!" (of type char const*)
|
|
[ FAILED ] ExampleTest.ShouldFail (0 ms)
|
|
[----------] 1 test from ExampleTest (0 ms total)
|
|
```
|
|
<!-- .element: class="fragment fade-in" -->
|
|
<!-- .slide: style="font-size: 0.85em" -->
|
|
___
|
|
<!-- .slide: data-background="#ccc" -->
|
|
## EXPECT_PRED* - comparison of floating point numbers
|
|
|
|
```C++
|
|
std::vector<std::pair<double, double>> vec{
|
|
{43.45, 43.45}, {44.34321, 45.1234}, {1.12, 1.1199999}};
|
|
|
|
for (const auto& [lhs, rhs] : vec) {
|
|
EXPECT_PRED_FORMAT2(testing::FloatLE, lhs, rhs);
|
|
EXPECT_PRED_FORMAT2(testing::DoubleLE, lhs, rhs);
|
|
}
|
|
```
|
|
<!-- .element: class="fragment fade-in" -->
|
|
|
|
```
|
|
[ RUN ] ExampleTest.ShouldFail
|
|
/home/mateusz.adamski/Documents/repoGit/advanced_cpp/examples/gtest.cpp:108: Failure
|
|
Expected: (lhs) <= (rhs)
|
|
Actual: 1.1200000000000001 vs 1.1199999
|
|
[ FAILED ] ExampleTest.ShouldFail (0 ms)
|
|
[----------] 1 test from ExampleTest (0 ms total)
|
|
|
|
[----------] Global test environment tear-down
|
|
```
|
|
<!-- .element: class="fragment fade-in" -->
|
|
<!-- .slide: style="font-size: 0.7em" -->
|
|
___
|
|
<!-- .slide: data-background="#ccc" -->
|
|
## GTEST_SKIP
|
|
|
|
```C++
|
|
TEST(ExampleTest, ShouldFail) {
|
|
GTEST_SKIP() << "Skipping test PR12345";
|
|
|
|
std::vector<std::pair<double, double>> vec{
|
|
{43.45, 43.45}, {44.34321, 45.1234}, {1.12, 1.1199999}};
|
|
|
|
for (const auto& [lhs, rhs] : vec) {
|
|
EXPECT_PRED_FORMAT2(testing::FloatLE, lhs, rhs);
|
|
EXPECT_PRED_FORMAT2(testing::DoubleLE, lhs, rhs);
|
|
}
|
|
}
|
|
```
|
|
<!-- .element: class="fragment fade-in" -->
|
|
|
|
```
|
|
[ RUN ] ExampleTest.ShouldFail
|
|
/home/mateusz.adamski/Documents/repoGit/advanced_cpp/examples/gtest.cpp:102: Skipped
|
|
Skipping test PR12345
|
|
```
|
|
<!-- .element: class="fragment fade-in" -->
|
|
<!-- .slide: style="font-size: 0.7em" -->
|
|
___
|
|
<!-- .slide: data-background="#ccc" -->
|
|
## Skipp all fixture
|
|
|
|
```C++
|
|
class BaseTestBlackListFirewall : public testing::Test {
|
|
public:
|
|
void SetUp() override {
|
|
GTEST_SKIP() << "Skipping test PR12345";
|
|
firewall_ = std::make_unique<BlackListFirewall>();
|
|
|
|
firewall_->addToBlacklist("192.201.1.12");
|
|
firewall_->addToBlacklist("192.122.1.12");
|
|
firewall_->addToBlacklist("192.178.1.12");
|
|
firewall_->addToBlacklist("192.144.1.12");
|
|
firewall_->addToBlacklist("192.188.1.12");
|
|
}
|
|
|
|
BlackListFirewall* firewall() const { return firewall_.get(); }
|
|
|
|
private:
|
|
std::unique_ptr<BlackListFirewall> firewall_;
|
|
};
|
|
```
|
|
<!-- .element: class="fragment fade-in" -->
|
|
|
|
```
|
|
[----------] 3 tests from BaseTestBlackListFirewall
|
|
[ RUN ] BaseTestBlackListFirewall.ShouldFilter
|
|
/home/mateusz.adamski/Documents/repoGit/advanced_cpp/examples/gtest.cpp:76: Skipped
|
|
Skipping test PR12345
|
|
[ SKIPPED ] BaseTestBlackListFirewall.ShouldFilter (0 ms)
|
|
[ RUN ] BaseTestBlackListFirewall.ShouldNotFilter
|
|
/home/mateusz.adamski/Documents/repoGit/advanced_cpp/examples/gtest.cpp:86: Skipped
|
|
Skipping test PR12345
|
|
[ SKIPPED ] BaseTestBlackListFirewall.ShouldNotFilter (0 ms)
|
|
[ RUN ] BaseTestBlackListFirewall.ShouldReportError
|
|
/home/mateusz.adamski/Documents/repoGit/advanced_cpp/examples/gtest.cpp:96: Skipped
|
|
Skipping test PR12345
|
|
[ SKIPPED ] BaseTestBlackListFirewall.ShouldReportError (0 ms)
|
|
[----------] 3 tests from BaseTestBlackListFirewall (0 ms total)
|
|
```
|
|
<!-- .element: class="fragment fade-in" -->
|
|
<!-- .slide: style="font-size: 0.60em" -->
|
|
___
|
|
<!-- .slide: data-background="#ccc" -->
|
|
## EXPECT_EXIT and EXPECT_THROW
|
|
|
|
```C++
|
|
TEST(MyDeathTest, NormalExit) {
|
|
EXPECT_EXIT(normalExit(), testing::ExitedWithCode(0), "Succes");
|
|
}
|
|
|
|
TEST(MyDeathTest, KillProcess) {
|
|
EXPECT_EXIT(killProcess(), testing::KilledBySignal(SIGKILL),
|
|
"Sending myself unblockable signal");
|
|
}
|
|
|
|
void foo(int val) {
|
|
if (val != 42) {
|
|
throw std::runtime_error("Wrong number");
|
|
}
|
|
}
|
|
|
|
TEST(MyDeathTest, NormalExit) {
|
|
EXPECT_THROW(foo(5), std::runtime_error);
|
|
}
|
|
```
|
|
<!-- .element: class="fragment fade-in" -->
|
|
<!-- .slide: style="font-size: 0.7em" -->
|
|
___
|
|
<!-- .slide: data-background="#ccc" -->
|
|
## Sharing Resources Between Tests
|
|
|
|
```C++
|
|
struct Foo {
|
|
std::string name_;
|
|
int value_;
|
|
};
|
|
|
|
class FooTest : public testing::Test {
|
|
public:
|
|
// Shared
|
|
static void SetUpTestSuite() {
|
|
if (!shared_resource_) {
|
|
shared_resource_ = std::make_unique<Foo>();
|
|
shared_resource_->name_ = "Shared Foo!\n";
|
|
shared_resource_->value_ = 42;
|
|
}
|
|
}
|
|
|
|
// Shared
|
|
static void TearDownTestSuite() {
|
|
shared_resource_ = nullptr;
|
|
}
|
|
|
|
// Per-test
|
|
void SetUp() override {}
|
|
|
|
// Per-test
|
|
void TearDown() override {}
|
|
|
|
protected:
|
|
inline static std::unique_ptr<Foo> shared_resource_;
|
|
};
|
|
|
|
TEST_F(FooTest, Test1) {
|
|
EXPECT_EQ(FooTest::shared_resource_->name_, "Shared Foo!\n");
|
|
EXPECT_EQ(FooTest::shared_resource_->value_, 42);
|
|
FooTest::shared_resource_->name_ = "Foo!!!";
|
|
FooTest::shared_resource_->value_ = 13;
|
|
}
|
|
|
|
TEST_F(FooTest, Test2) {
|
|
EXPECT_EQ(FooTest::shared_resource_->name_, "Foo!!!");
|
|
EXPECT_EQ(FooTest::shared_resource_->value_, 13);
|
|
}
|
|
```
|
|
<!-- .element: class="fragment fade-in" -->
|
|
<!-- .slide: style="font-size: 0.7em" -->
|
|
___
|
|
<!-- .slide: data-background="#ccc" -->
|
|
## Testing private code - should be avoided
|
|
|
|
```C++
|
|
class Foo {
|
|
private:
|
|
FRIEND_TEST(FooTest, PrivateTest);
|
|
|
|
int doSth(int val) { return val; }
|
|
};
|
|
|
|
TEST(FooTest, PrivateTest) {
|
|
Foo foo;
|
|
EXPECT_EQ(foo.doSth(42), 42);
|
|
}
|
|
```
|
|
<!-- .element: class="fragment fade-in" -->
|
|
<!-- .slide: style="font-size: 0.9em" -->
|
|
___
|
|
|
|
## Exercise 1
|
|
|
|
Open project **SHM** and write test for class `Ship.cpp` which will test it's interface. Remember to test edge cases :). Test it well and fix issues.
|
|
|
|
Go to `Presentation/exercises/SHM/build/test` and run test using command `./SHM_Tests`.
|