User Tools

Site Tools


unit_tests

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
Last revisionBoth sides next revision
unit_tests [2016/09/08 14:37] mhatzunit_tests [2016/09/09 07:38] mhatz
Line 5: Line 5:
 For Mail2Voice Next, we introduced unit tests. Units tests are useful to ensure that every classes/methods/functions behave the way we think they are supposed to. It helps the developers to design their code when writing the tests before coding the actual implementation. It helps the developers verifying that the code still works after modifications. Ultimately, it helps developers to detect regressions in the code. Unit tests are one the first tools to use in Quality Assurance. For Mail2Voice Next, we introduced unit tests. Units tests are useful to ensure that every classes/methods/functions behave the way we think they are supposed to. It helps the developers to design their code when writing the tests before coding the actual implementation. It helps the developers verifying that the code still works after modifications. Ultimately, it helps developers to detect regressions in the code. Unit tests are one the first tools to use in Quality Assurance.
  
-The unit tests written for Mail2Voice are base on QTest suite.+The unit tests written for Mail2Voice are based on QTest suite.
  
-===== HOWTO writing a unit test for a class =====+===== HOWTO writing a unit test for a class =====
  
-Each class of Mail2Voice must have its dedicated unit tests to check every method in relevant scenarios (to test edge cases, side effects, etc.).+Each class of Mail2Voice must have its dedicated unit tests to check every methods in relevant scenarios (to test edge cases, side effects, etc.).
  
 ==== Example ==== ==== Example ====
Line 34: Line 34:
 </code> </code>
  
-MyClass is a very simple class that does two things : incrementing an internal variable and setting an internal text. Nothing special there, isn't it? Why should we care about testing such a trivial code?+MyClass is a very simple class that does two things : incrementing an internal variable and setting an internal text. Nothing special here, isn't it? Why should we care about testing such a trivial code?
  
-Wait, mistakes are common and even if you are sure of your code, are you sure that no one else won't introduce errors in your code? That, is where unit tests are useful : ensuring your code is correct and will stay correct over time. Saving you a lot of time of debugging in the future.+Wait, mistakes are common and even if you are sure of your code, are you sure that no one else won't introduce errors in your code? That, is where unit tests are useful: ensuring your code is correct and will stay correct over time. Saving you a lot of time of debugging in the future.
  
 ==== What to test? ==== ==== What to test? ====
Line 50: Line 50:
  
   * MyClass(): the constructor must initialize the object, so it must initialize the members m_count and m_text. But, to which values? This have to be specified somewhere otherwise the objects could be inconsistent.   * MyClass(): the constructor must initialize the object, so it must initialize the members m_count and m_text. But, to which values? This have to be specified somewhere otherwise the objects could be inconsistent.
-    * For our example, we will pretend that m_count must be initialize to 0 and m_text to "default text";+    * For our example, we will pretend that m_count must be initialized to 0 and m_text to "Default text";
  
-  * increment(): this must add just 1 to m_count. But m_count is an int, meaning it can be negative. What if we increment m_count until 2147483647 (assuming int is on 32 bits) and the increment again? Does the increment method will just put m_count to the minimum negative value? Or will it refuse to increment again, leaving m_count to 2147483647?+  * increment(): this must add just 1 to m_count. But m_count is an int, meaning it can be negative. What if we increment m_count until 2147483647 (assuming int is on 32 bits) and then increment again? Does the increment method will just put m_count to the minimum negative value (-2147483648)? Or will it refuse to increment again, leaving m_count to 2147483647?
     * For our example, we choose the second option: never go to negative value.     * For our example, we choose the second option: never go to negative value.
  
-  * setText(const QString& text): now you get it, we will assume this method accept any strings except empty ones.+  * setText(const QString& text): now you get it, we will assume this method accept any strings except empty ones (it will do nothing in this case).
  
   * count() and text() methods must always return the current value.   * count() and text() methods must always return the current value.
Line 88: Line 88:
 </code> </code>
  
-The PropsTester class is a facility to check properties against their default values. While it is not mandatory, it is highly recommended to ease the test of class members integrity.+The PropsTester class is a convenience to check properties against their default values. While it is not mandatory, it is highly recommended to ease the test of class members integrity.
  
 The implementation of TestMyClass will look like this: The implementation of TestMyClass will look like this:
Line 99: Line 99:
 TestMyClass::TestMyClass(QObject *parent) : QObject(parent) TestMyClass::TestMyClass(QObject *parent) : QObject(parent)
 { {
-    // We append to the m_propsCheckList some lists that contain a name and a lambda function that check the default value of class members.+    // m_propsCheckList stores lambdas functions that check the default value of class members
 +    // Each lambda function is associated with a name ("count" and "text" here) representing the tested class member.
     m_propsChecklist.append({"count", [](const MyClass& myClass) { QVERIFY2(myClass.count() == 0, "Count has not been initialized to 0"); }});     m_propsChecklist.append({"count", [](const MyClass& myClass) { QVERIFY2(myClass.count() == 0, "Count has not been initialized to 0"); }});
     m_propsChecklist.append({"text", [](const MyClass& myClass) { QVERIFY2(myClass.text() == QString("Default text"), "Text is not set to default."); }});     m_propsChecklist.append({"text", [](const MyClass& myClass) { QVERIFY2(myClass.text() == QString("Default text"), "Text is not set to default."); }});
Line 120: Line 121:
         myClass.increment();         myClass.increment();
         QVERIFY2(myClass.count() == i, "Count is incorrect.");         QVERIFY2(myClass.count() == i, "Count is incorrect.");
-        checkDefaultProperties(account, {"count"}); // We check that all members are set to default values except m_count+        checkDefaultProperties(myClass, {"count"}); // We check that all members are set to default values except m_count
     }     }
 } }
Line 128: Line 129:
     MyClass myClass;     MyClass myClass;
          
-    for(int i = 1; i <2147483647; ++i)+    for(int i = 0; i < 2147483647; ++i)
     {     {
         myClass.increment();         myClass.increment();
Line 138: Line 139:
     QVERIFY2(myClass.count() == 2147483647, "Count is incorrect."); // Check again     QVERIFY2(myClass.count() == 2147483647, "Count is incorrect."); // Check again
          
-    checkDefaultProperties(account, {"count"}); // Check that nothing else has changed due to side effects+    checkDefaultProperties(myClass, {"count"}); // Check that nothing else has changed due to side effects
 } }
  
Line 146: Line 147:
     myClass.setText("Hello world!");     myClass.setText("Hello world!");
     QVERIFY2(myClass.text() == "Hello world!", "Text incorrect."); // Check the text has been set properly     QVERIFY2(myClass.text() == "Hello world!", "Text incorrect."); // Check the text has been set properly
 +    checkDefaultProperties(myClass, {"text"}); // Check that nothing else has changed due to side effects
 } }
  
Line 153: Line 155:
     myClass.setText("");     myClass.setText("");
     QVERIFY2(myClass.text() == "Default text", "Text has been changed."); // Check that the text has not been changed at all.     QVERIFY2(myClass.text() == "Default text", "Text has been changed."); // Check that the text has not been changed at all.
 +    checkDefaultProperties(myClass, {"text"}); // Check that nothing else has changed due to side effects
 } }
 </code> </code>
  
  
 +Then, in the main.cpp file of the unittests subproject, you have to instantiate a TestMyClass object and add it to the list of tests to run:
  
 +<code cpp>
 +int main( int argc, char *argv[])
 +{
 +    int ret = 0;
 +
 +    TestContact tstContact;
 +    TestEmail tstEmail;
 +    TestAccount tstAccount;
 +    TestAttachment tstAttachment;
 +    TestServerSettings tstServerSettings;
 +    TestMyClass tstMyClass; // <-- instantiation
 +
 +    ret = executeTests(argc, argv,
 +                      {&tstContact,
 +                       &tstEmail,
 +                       &tstAccount,
 +                       &tstAttachment,
 +                       &tstServerSettings,
 +                       &tstMyClass}); // Add test to the suite
 +
 +    return ret;
 +}
 +</code>
 +
 +==== Running unit tests ====
  
 +Well, that is the simplest part, just select the unittests subproject in QtCreator, compile it and run it!
unit_tests.txt · Last modified: 2023/04/25 16:52 by 127.0.0.1