Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

KmTest: Kernel-mode C++ Unit Testing Framework in BDD-style

0.00/5 (No votes)
18 Jan 2017 1  
How-to guide about using KmTest for writing kernel-mode unit tests
This article is targeted at Windows driver developers and discusses a unit testing framework that works in OS kernel.

Introduction

There is a lack of unit testing frameworks that work in OS kernel. This library closes that gap and is targeted for Windows driver developers.

Features

  • Designed for testing kernel-mode code
  • Header-only
  • Easy to use
  • BDD-style approach for writing unit tests (as well as a traditional one)
  • Code sharing between steps in scenario

Requirements

  • Windows XP and higher
  • Visual Studio 2010 and higher

Usage

Creating a Test Project

Create an empty driver project and do the following:

  • Add a path to kmtest/include into the project include paths.
  • Add #include <kmtest/kmtest.h> into your new cpp/h files (if you have precompiled headers, it is a good place to add this include there).

This is a sample precompiled header:

C++
#pragma once
#include <ntddk.h>
#include <kmtest/kmtest.h>

Now you can start writing tests.

Note: DriverEntry is automatically created by the library, so you don't need to write it.

Writing Tests

You can write tests cases in two styles:

  • BDD-style (using GIVEN-WHEN-THEN clauses)
  • traditional

BDD-style Test

BDD-style tests require more efforts in writing but they are superior in maintaining than traditional tests. The basic test structure is shown below (for more advanced usage, read about code sharing):

C++
SCENARIO("Addition operation")
{
    GIVEN("x = 2")
    {
        int x = 2;

        WHEN("y = 3")
        {
            int y = 3;

            THEN("the sum will be 5")
            {
                REQUIRE(Calculator::add(x, y) == 5);
            }
        }
    }
}

where:

  • SCENARIO, GIVEN, WHEN, THEN are used to describe the test
  • REQUIRE is used for assertions (can be placed in any block)
Code Sharing

A great feature of BDD-style tests is that a SCENARIO can have several GIVEN clauses, a GIVEN can have several WHEN clauses, a WHEN can have several THEN clauses. KmTest framework will run all combinations as independent test cases. The sample below will produce two test cases (2+3=5 and 2+0=2):

C++
SCENARIO("Addition operation")
{
    GIVEN("x = 2")
    {
        int x = 2;

        WHEN("y = 3")
        {
            int y = 3;

            THEN("the sum will be 5")
            {
                REQUIRE(Calculator::add(x, y) == 5);
            }
        }

        WHEN("y = 0")
        {
            int y = 0;

            THEN("the sum will be 2")
            {
                REQUIRE(Calculator::add(x, y) == 2);
            }
        }
    }
}    

That's not all. Setup/cleanup code can be shared as well. It is demonstrated by the following example:

C++
SCENARIO("Addition operation")
{
    // <== Here, you can write a shared setup code for SCENARIO.

    GIVEN("x = 2")
    {
        int x = 2; // <== Here, you can write a shared setup code for GIVEN.

        WHEN("y = 3")
        {
            int y = 3; // <== Here, you can write a shared setup code for WHEN.

            THEN("the sum will be 5")
            {
                REQUIRE(Calculator::add(x, y) == 5);
            }

            // <== Here, you can write a shared cleanup code for WHEN.
        }

        // <== Here, you can write a shared cleanup code for GIVEN.
    }

    // <== Here, you can write a shared cleanup code for SCENARIO.
}

Traditional Test

A traditional test is shown below and is represented by a BDD-style test without GIVEN-WHEN-THEN clauses:

C++
// A minimal scenario for those who do not want to write GIVEN-WHEN-THEN clauses.
SCENARIO("Multiplication operation")
{
    REQUIRE(6 == Calculator::mul(2, 3));
    REQUIRE(-30 == Calculator::mul(-10, 3));
    REQUIRE(6 == Calculator::mul(-2, -3));
    REQUIRE(0 == Calculator::mul(0, 3));
}

where:

  • SCENARIO is used to describe the test
  • REQUIRE is used for assertions

Running Tests

Running KmTest based tests means starting a driver. It is highly recommended to do this inside a virtual machine. Any assertion failure will trigger a kernel debugger breakpoint or a BSOD if there is no debugger.

Refer to samples/CalcTest/CalcTest.cmd for how to start a driver from the command line.

Test Output

KmTest writes messages to the debug output. It can be viewed by WinDbg, DbgView or similar tools. A sample test output is demonstrated below:

**************************************************
* KMTEST BEGIN
**************************************************
--------------------------------------------------
SCENARIO: Addition operation
--------------------------------------------------
GIVEN: x = 2
  WHEN: y = 3
    THEN: the sum will be 5
GIVEN: x = 2
  WHEN: y = 0
    THEN: the sum will be 2
GIVEN: x = 2
  WHEN: y = -2
    THEN: the sum will be 0
GIVEN: x = -2
  WHEN: y = 3
    THEN: the sum will be 1
GIVEN: x = -2
  WHEN: y = -1
    THEN: the sum will be -3

ASSERTIONS PASSED: 5

--------------------------------------------------
SCENARIO: Multiplication operation
--------------------------------------------------

ASSERTIONS PASSED: 4

--------------------------------------------------
SCENARIO: Subtraction operation
--------------------------------------------------
GIVEN: x = 8
  WHEN: y = 3
    THEN: the difference will be 5
GIVEN: x = 8
  WHEN: y = 0
    THEN: the difference will be 8
GIVEN: x = 8
  WHEN: y = -2
    THEN: the difference will be 10
GIVEN: x = -3
  WHEN: y = 2
    THEN: the difference will be -5
GIVEN: x = -3
  WHEN: y = -1
    THEN: the difference will be -2

ASSERTIONS PASSED: 5

**************************************************
* KMTEST END (scenarios: 3, assertions: 14)
************************************************** 

Samples

There is a samples folder that demonstrates usage of KmTest unit testing framework. To compile it, you need Visual Studio 2015 and WDK10.

GitHub

The project is available on GitHub at https://github.com/SergiusTheBest/kmtest.

Acknowledgment

Thanks to Phil Nash and his Catch C++ test framework for BDD-style inspiration.

License Note

KmTest is licensed under the MPL version 2.0. You can freely use it in your commercial or opensource software.

History

  • 19th January, 2017: Initial version

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here