Unit Testing - dagelf/synergy-core GitHub Wiki
To unit test Synergy, we use googletest (Google C++ Testing Framework), also known as gtest.
- Test classes (or "suites") must:
- Be kept in either:
-
src/test/unittests
(for unit tests) -
src/test/integtests
(for integration tests)
-
- Be named like so:
{class-to-test}Tests
(e.g.CClipboardTests
). - Test the logic of only 1 class (other classes must be mocked).
- Be kept in either:
- Test functions must:
- Be named like so:
{function-to-test}_{scenario}_{expectation}
- Follow either:
- The AAA pattern, arrange-act-assert (see example below).
- Or, arrange-expect-act (see example below).
- Test only 1 function in the "act" stage.
- Have either:
- A single assert (at the end), with some rare exceptions.
- Or, a single EXPECT_CALL (before the call to the method under test).
- Be named like so:
#include <gtest/gtest.h> #include "CClipboard.h" TEST(CClipboardTests, getTime_defaultState_returnsZero) { // arrange CClipboard clipboard; // act CClipboard::Time actual = clipboard.getTime(); // assert EXPECT_EQ(0, actual); }
These are less favoured, since you should try to test the result of a function, not the internal behaviour. But some times (especially with legacy code) this is the only practical option.
#include <gtest/gtest.h> #include <gmock/gmock.h> #include "CKeyStateTests.h" #include "CMockEventQueue.h" #include "CMockKeyMap.h" #include "CKeyStateImpl.h" using ::testing::_; using ::testing::NiceMock; TEST(CKeyStateTests, sendKeyEvent_keyRepeat_addEventCalledOnce) { // arrange NiceMock<CMockKeyMap> keyMap; NiceMock<CMockEventQueue> eventQueue; CKeyStateImpl keyState(eventQueue, keyMap); // expect EXPECT_CALL(eventQueue, addEvent(_)).Times(1); // act keyState.sendKeyEvent(NULL, false, true, 1, 0, 0, 0); }
There is an interesting problem with writing unit tests for Synergy.
Each unit test should only test one component; if you test logic that uses the clipboard for example, then you're testing multiple components (both Synergy and the OS). A test that exercises multiple components is **not a true unit test**, but is in fact it is an integration test (in my opinion).
So, any logic that ventures into non-Synergy territory, such as most of the code in CMSWindowsClipboard, should be tested with an integration test.
I think that this can only be a good thing, because a developer will think twice about running integration tests, since it will cause strange things to happen, such as:
- Where the hell has my clipboard data gone?
- Why did my mouse just jump across my screen?
- Where are all these random characters coming from?!
- etc...
The tests are part of the existing build tool chain; so just build normally to build the unit tests.
- Windows:
- Unit tests:
bin\Debug\unittests.exe
- Integration tests:
bin\Debug\integtests.exe
- Unit tests:
- Linux/Mac:
- Unit tests:
bin/debug/unittests
- Integration tests:
bin/debug/integtests
- Unit tests:
Use the filter argument to run specific tests:
- Example:
bin\debug\unittests.exe --gtest_filter=CClipboardTests*
Use the --help
argument.
- Example:
bin\debug\unittests.exe --help
>bin\debug\unittests.exe Running main() from gtest_main.cc [==========] Running 26 tests from 2 test cases. [----------] Global test environment set-up. [----------] 25 tests from CClipboardTests [ RUN ] CClipboardTests.empty_openCalled_returnsTrue [ OK ] CClipboardTests.empty_openCalled_returnsTrue (0 ms) [ RUN ] CClipboardTests.empty_singleFormat_hasReturnsFalse [ OK ] CClipboardTests.empty_singleFormat_hasReturnsFalse (0 ms) [ RUN ] CClipboardTests.add_newValue_valueWasStored [ OK ] CClipboardTests.add_newValue_valueWasStored (0 ms) [ RUN ] CClipboardTests.add_replaceValue_valueWasReplaced [ OK ] CClipboardTests.add_replaceValue_valueWasReplaced (0 ms) // // tests omitted to keep the example concise // [ RUN ] CClipboardTests.copy_withSingleText_clipboardsAreEqual [ OK ] CClipboardTests.copy_withSingleText_clipboardsAreEqual (0 ms) [----------] 25 tests from CClipboardTests (13 ms total) [----------] 1 test from HelloWorldTests [ RUN ] HelloWorldTests.helloWorld [ OK ] HelloWorldTests.helloWorld (0 ms) [----------] 1 test from HelloWorldTests (0 ms total) [----------] Global test environment tear-down [==========] 26 tests from 2 test cases ran. (15 ms total) [ PASSED ] 26 tests.