Code Monkey home page Code Monkey logo

cut's Introduction

CUT: C++ Unified Test Framework

Cut is a simple, practical, and scalable xUnit Test Framework in Modern C++11. It's inspiration comes from the famous testing framework JUnit in Java community.

C/C++ are different languages from most modern ones. Writing tests for them has some very specific challenges. Therefore, simply clone ideas from xUnit frameworks for other languages does not work the best. Programmers have to suffer accidental complexities brought by most of existing frameworks.

Cut is designed for simplifying efforts of programers, in terms of development, maintenance, flexibility of test management, build & run-time strategy, and others.

Requirement

  • Supported Platform:

    • [MAC OS X] supported
    • [Linux] supported
    • [Windows] partial supported
  • Supported Compilers:

    • [CLANG] 3.4 or later.
    • [GCC] 4.8 or later.
    • [MinGW] ok
    • [Cygwin] ok
    • [MSVC] not supported.

Installing

Cut support 2 intalling method.

  • Online Installing
  • Manual Installing

Online Installing

$ curl -fsSL https://raw.github.com/ccock/cut/master/install.sh | sh

Manual Installing and Testing

Install Cut
$ mkdir tmp && cd tmp
$ cmake .. && make
$ sudo make install 
Test cut
$ cd tmp 
$ cmake -DENABLE_TEST=on .. && make
$ test/cut-test

Build on Windows

MinGW
$ cmake -G"MinGW Makefiles" ..
$ make
$ make install
Cygwin
$ cmake ..
$ make
$ make install

Begin the new journey

Ice Breaking

Physical Directory
quantity
├── include
│   └── quantity
├── src
│   └── quantity
└── test
│   ├── main.cpp
└── CMakeLists.txt
main
#include <cut/cut.hpp>

int main(int argc, char** argv)
{
    return cut::run_all_tests(argc, argv);
}
CMakeList
project(quantity)

cmake_minimum_required(VERSION 2.8)

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)

file(GLOB_RECURSE all_files
src/*.cpp
src/*.cc
src/*.c
test/*.cpp
test/*.cc
test/*.c)

add_executable(quantity-test ${all_files})

target_link_libraries(quantity-test cut)
Build
$ mkdir build
$ cd build
$ cmake ..
$ make
Run Test
$ ./quantity-test

[==========] Running 0 test cases.
[----------] 0 tests from All Tests
[----------] 0 tests from All Tests

[==========] 0 test cases ran.
[  TOTAL   ] PASS: 0  FAILURE: 0  ERROR: 0  TIME: 0 us

cut Experience

First Test Case

#include <cut/cut.hpp>

#include <quantity/Length.h>

USING_CUM_NS

FIXTURE(LengthTest)
{
    TEST("1 FEET should equal to 12 INCH")
    {
        ASSERT_THAT(Length(1, FEET), eq(Length(12, INCH)));
    }
};

Length Implements

// quantity/Length.h
#include <quantity/Amount.h>

enum LengthUnit
{
    INCH = 1,
    FEET = 12 * INCH,
};

struct Length
{
    Length(Amount amount, LengthUnit unit);

    bool operator==(const Length& rhs) const;
    bool operator!=(const Length& rhs) const;

private:
    const Amount amountInBaseUnit;
};
// quantity/Length.cpp
#include <quantity/Length.h>

Length::Length(Amount amount, LengthUnit unit)
  : amountInBaseUnit(unit * amount)
{
}

bool Length::operator==(const Length& rhs) const
{
    return amountInBaseUnit == rhs.amountInBaseUnit;
}

bool Length::operator!=(const Length& rhs) const
{
    return !(*this == rhs);
}
Build
$ mkdir build
$ cd build
$ cmake ..
$ make
Run Test
$ ./quantity-test

[==========] Running 1 test cases.
[----------] 1 tests from All Tests
[----------] 1 tests from LengthTest
[ RUN      ] LengthTest::1 FEET should equal to 12 INCH
[       OK ] LengthTest::1 FEET should equal to 12 INCH(13 us)
[----------] 1 tests from LengthTest

[----------] 1 tests from All Tests

[==========] 1 test cases ran.
[  TOTAL   ] PASS: 1  FAILURE: 0  ERROR: 0  TIME: 13 us

Fixture

Test fixture can be divided into three categories:

  • Independent Fixture
  • Shared Fixture
  • Global Fixture.

BDD Style

xUnit BDD
FIXTURE CONTEXT
SETUP BEFORE
TEARDOWN AFTER
ASSERT_THAT EXPECT

Independent Fixture

#include <cut/cut.hpp>

FIXTURE(LengthTest)
{
    Length length;

    SETUP()
    {}

    TEARDOWN()
    {}

    TEST("length test1")
    {}

    TEST("length test2")
    {}
};

Executing sequence:

  1. Length Constructor
  2. SETUP
  3. TEST("length test1")
  4. TEARDOWN
  5. Length Destructor
  6. Length Constructor
  7. SETUP
  8. TEST("length test2")
  9. TEARDOWN
  10. Length Destructor

Shared Fixture

#include <cut/cut.hpp>

FIXTURE(LengthTest)
{
    Length length;

    BEFORE_CLASS()
    {}

    AFTER_CLASS()
    {}

    BEFORE()
    {}

    AFTER()
    {}

    TEST("length test1")
    {}

    TEST("length test2")
    {}
};

Executing squence:

  1. BEFORE_CLASS
  2. Length Constructor
  3. BEFORE
  4. TEST("length test1")
  5. AFTER
  6. Length Destructor
  7. Length Constructor
  8. BEFORE
  9. TEST("length test2")
  10. AFTER
  11. Length Destructor
  12. AFTER_CLASS

Global Fixture

#include <cut/cut.hpp>

BEFORE_ALL("before all 1")
{
}

BEFORE_ALL("before all 2")
{
}

AFTER_ALL("after all 1")
{
}

AFTER_ALL("after all 2")
{
}
#include <cut/cut.hpp>

FIXTURE(LengthTest)
{
    Length length;

    BEFORE_CLASS()
    {}

    AFTER_CLASS()
    {}

    BEFORE()
    {}

    AFTER()
    {}

    TEST("length test1")
    {}

    TEST("length test2")
    {}
};
#include <cut/cut.hpp>

FIXTURE(VolumeTest)
{
    Volume volume;

    BEFORE_CLASS()
    {}

    AFTER_CLASS()
    {}

    BEFORE()
    {}

    AFTER()
    {}

    TEST("volume test1")
    {}

    TEST("volume test1")
    {}
};

Possible executing squence:

  1. BEFORE_ALL("before all 1")
  2. BEFORE_ALL("before all 2")
  3. LengthTest::BEFORE_CLASS
  4. Length Constructor
  5. LengthTest::BEFORE
  6. TEST("length test1")
  7. LengthTest::AFTER
  8. Length Destructor
  9. Length Constructor
  10. LengthTest::BEFORE
  11. TEST("length test2")
  12. LengthTest::AFTER
  13. Length Destructor
  14. LengthTest::AFTER_CLASS
  15. VolumeTest::BEFORE_CLASS
  16. Volume Constructor
  17. LengthTest::BEFORE
  18. TEST("volume test1")
  19. LengthTest::AFTER
  20. Volume Destructor
  21. Volume Constructor
  22. LengthTest::BEFORE
  23. TEST("volume test2")
  24. LengthTest::AFTER
  25. Volume Destructor
  26. VolumeTest::AFTER_CLASS
  27. AFTER_ALL("after all 2")
  28. AFTER_ALL("after all 1")

Case Design

Auto Identification

#include <cut/cut.hpp>
#include <quantity/length/Length.h>

USING_CUM_NS

FIXTURE(LengthTest)
{
    TEST("1 FEET should equal to 12 INCH")
    {
        ASSERT_THAT(Length(1, FEET), eq(Length(12, INCH)));
    }

    TEST("1 YARD should equal to 3 FEET")
    {
        ASSERT_THAT(Length(1, YARD), eq(Length(3, FEET)));
    }

    TEST("1 MILE should equal to 1760 YARD")
    {
        ASSERT_THAT(Length(1, MILE), eq(Length(1760, YARD)));
    }
};

OO

A independant and new instance RobotCleaner robot will be created for each test case.

#include <cut/cut.hpp>
#include <robot-cleaner/RobotCleaner.h>
#include <robot-cleaner/Position.h>
#include <robot-cleaner/Instructions.h>

USING_CUM_NS

FIXTURE(RobotCleanerTest)
{
    RobotCleaner robot;

    TEST("at the beginning, the robot should be in at the initial position")
    {
        ASSERT_THAT(robot.getPosition(), is(Position(0, 0, NORTH)));
    }

    TEST("left instruction: 1-times")
    {
        robot.exec(left());
        ASSERT_THAT(robot.getPosition(), is(Position(0, 0, WEST)));
    }

    TEST("left instruction: 2-times")
    {
        robot.exec(left());
        robot.exec(left());
        ASSERT_THAT(robot.getPosition(), is(Position(0, 0, SOUTH)));
    }
};

Extract Method

#include <cut/cut.hpp>
#include <robot-cleaner/RobotCleaner.h>
#include <robot-cleaner/Position.h>
#include <robot-cleaner/Instructions.h>

USING_CUM_NS

FIXTURE(RobotCleanerTest)
{
    RobotCleaner robot;

    void WHEN_I_send_instruction(Instruction* instruction)
    {
        robot.exec(instruction);
    }

    void AND_I_send_instruction(Instruction* instruction)
    {
        WHEN_I_send_instruction(instruction);
    }

    void THEN_the_robot_cleaner_should_be_in(const Position& position)
    {
        ASSERT_THAT(robot.getPosition(), is(position));
    }

    TEST("at the beginning")
    {
        THEN_the_robot_cleaner_should_be_in(Position(0, 0, NORTH));
    }

    TEST("left instruction: 1-times")
    {
        WHEN_I_send_instruction(left());
        THEN_the_robot_cleaner_should_be_in(Position(0, 0, WEST));
    }

    TEST("left instruction: 2-times")
    {
        WHEN_I_send_instruction(repeat(left(), 2));
        THEN_the_robot_cleaner_should_be_in(Position(0, 0, SOUTH));
    }

    TEST("left instruction: 3-times")
    {
        WHEN_I_send_instruction(repeat(left(), 3));
        THEN_the_robot_cleaner_should_be_in(Position(0, 0, EAST));
    }

    TEST("left instruction: 4-times")
    {
        WHEN_I_send_instruction(repeat(left(), 4));
        THEN_the_robot_cleaner_should_be_in(Position(0, 0, NORTH));
    }
};

Assertion

ASSERT_THAT

ASSERT_THAT makes assertions more expressive, it will be the actual value on the left, but the desired value on the right, and more consistent with English habits.

#include <cut/cut.hpp>

FIXTURE(CloseToTest)
{
    TEST("close to double")
    {
        ASSERT_THAT(1.0, close_to(1.0, 0.5));
        ASSERT_THAT(0.5, close_to(1.0, 0.5));
        ASSERT_THAT(1.5, close_to(1.0, 0.5));
    }
};

Hamcrest

Hamcrest is a lightweight, extensible matcher framework that has been introduced into the JUnit framework by Beck Kent. cut introduces the design of Hamcrest, implements a C++ porting version of Hamcrest, which makes the assertion of cutg more scalable and readable.

anything
Matcher Description
anything always matched
_ syntax sugar of anything
#include <cut/cut.hpp>

USING_CUM_NS

FIXTURE(AnythingTest)
{
    TEST("should always be matched")
    {
        ASSERT_THAT(1, anything<int>());
        ASSERT_THAT(1u, anything<unsigned int>());
        ASSERT_THAT(1.0, anything<double>());
        ASSERT_THAT(1.0f, anything<float>());
        ASSERT_THAT(false, anything<bool>());
        ASSERT_THAT(true, anything<bool>());
        ASSERT_THAT(nullptr, anything<std::nullptr_t>());
    }

    TEST("should support _ as syntactic sugar")
    {
        ASSERT_THAT(1u, _(int));
        ASSERT_THAT(1.0f, _(float));
        ASSERT_THAT(false, _(int));
        ASSERT_THAT(nullptr, _(std::nullptr_t));
    }
};
Comparator
Matcher Description
eq equal to
ne not equal to
lt less than
gt greater than
le less or equal to
ge greater or equal to
#include <cut/cut.hpp>

USING_CUM_NS

FIXTURE(EqualToTest)
{
    TEST("should allow compare to integer")
    {
        ASSERT_THAT(0xFF, eq(0xFF));
        ASSERT_THAT(0xFF, is(eq(0xFF)));

        ASSERT_THAT(0xFF, is(0xFF));
        ASSERT_THAT(0xFF == 0xFF, is(true));
    }

    TEST("should allow compare to bool")
    {
        ASSERT_THAT(true, eq(true));
        ASSERT_THAT(false, eq(false));
    }

    TEST("should allow compare to string")
    {
        ASSERT_THAT("hello", eq("hello"));
        ASSERT_THAT("hello", eq(std::string("hello")));
        ASSERT_THAT(std::string("hello"), eq(std::string("hello")));
    }
};

FIXTURE(NotEqualToTest)
{
    TEST("should allow compare to integer")
    {
        ASSERT_THAT(0xFF, ne(0xEE));

        ASSERT_THAT(0xFF, is_not(0xEE));
        ASSERT_THAT(0xFF, is_not(eq(0xEE)));
        ASSERT_THAT(0xFF != 0xEE, is(true));
    }

    TEST("should allow compare to boolean")
    {
        ASSERT_THAT(true, ne(false));
        ASSERT_THAT(false, ne(true));
    }

    TEST("should allow compare to string")
    {
        ASSERT_THAT("hello", ne("world"));
        ASSERT_THAT("hello", ne(std::string("world")));
        ASSERT_THAT(std::string("hello"), ne(std::string("world")));
    }
};
Decorator
Matcher Description
is is decorator
is_not not decorator
#include <cut/cut.hpp>

USING_CUM_NS

FIXTURE(IsNotTest)
{
    TEST("integer")
    {
        ASSERT_THAT(0xFF, is_not(0xEE));
        ASSERT_THAT(0xFF, is_not(eq(0xEE)));
    }

    TEST("string")
    {
        ASSERT_THAT("hello", is_not("world"));
        ASSERT_THAT("hello", is_not(eq("world")));

        ASSERT_THAT("hello", is_not(std::string("world")));
        ASSERT_THAT(std::string("hello"), is_not(std::string("world")));
    }
};
NULL Pointer
Matcher Description
nil null pointer
#include <cut/cut.hpp>

USING_CUM_NS

FIXTURE(NilTest)
{
    TEST("equal_to")
    {
        ASSERT_THAT(nullptr, eq(nullptr));
        ASSERT_THAT(0, eq(NULL));
        ASSERT_THAT(NULL, eq(NULL));
        ASSERT_THAT(NULL, eq(0));
    }

    TEST("is")
    {
        ASSERT_THAT(nullptr, is(nullptr));
        ASSERT_THAT(nullptr, is(eq(nullptr)));

        ASSERT_THAT(0, is(0));
        ASSERT_THAT(NULL, is(NULL));
        ASSERT_THAT(0, is(NULL));
        ASSERT_THAT(NULL, is(0));
    }

    TEST("nil")
    {
        ASSERT_THAT((void*)NULL, nil());
        ASSERT_THAT((void*)0, nil());
        ASSERT_THAT(nullptr, nil());
    }
};
String
Matcher Description
contains_string contains a sub-string
contains_string_ignoring_case contains a sub-string ignoring case
starts_with starts with a sub-string
starts_with_ignoring_case starts with a sub-string ignoring case
ends_with ends with a sub-string
ends_with_ignoring_case ends with a sub-string ignoring case
#include <cut/cut.hpp>

USING_CUM_NS

FIXTURE(StartsWithTest)
{
    TEST("case sensitive")
    {
        ASSERT_THAT("ruby-cpp", starts_with("ruby"));
        ASSERT_THAT("ruby-cpp", is(starts_with("ruby")));

        ASSERT_THAT(std::string("ruby-cpp"), starts_with("ruby"));
        ASSERT_THAT("ruby-cpp", starts_with(std::string("ruby")));
        ASSERT_THAT(std::string("ruby-cpp"), starts_with(std::string("ruby")));
    }

    TEST("ignoring case")
    {
        ASSERT_THAT("ruby-cpp", starts_with_ignoring_case("Ruby"));
        ASSERT_THAT("ruby-cpp", is(starts_with_ignoring_case("Ruby")));

        ASSERT_THAT(std::string("ruby-cpp"), starts_with_ignoring_case("RUBY"));
        ASSERT_THAT("Ruby-Cpp", starts_with_ignoring_case(std::string("rUBY")));
        ASSERT_THAT(std::string("RUBY-CPP"), starts_with_ignoring_case(std::string("ruby")));
    }
};
Float
Matcher Description
close_to close to a float number
nan not a number
#include <cut/cut.hpp>
#include <math.h>

USING_CUM_NS

FIXTURE(IsNanTest)
{
    TEST("double")
    {
        ASSERT_THAT(sqrt(-1.0), nan());
        ASSERT_THAT(sqrt(-1.0), is(nan()));

        ASSERT_THAT(1.0/0.0,  is_not(nan()));
        ASSERT_THAT(-1.0/0.0, is_not(nan()));
    }
};

cut Options

TestOptions::TestOptions() : desc("cut")
{
    desc.add({
        {"help,     h",   "help message"},
        {"filter,   f",   "--filter=pattern"},
        {"color,    c",   "--color=[yes|no]"},
        {"xml,      x",   "print test result into XML file"},
        {"list,     l",   "list all tests without running them"},
        {"progress, p",   "print test result in progress bar"},
        {"verbose,  v",   "verbosely list tests processed"},
        {"repeat,   r",   "how many times to repeat each test"}
    });
    
    // default value
    opt["color"]  = "yes";
    opt["repeat"] = "1";
}

Copyright

Copyright (c) 2015-2020 Horance Liu. See LICENSE for details.

cut's People

Contributors

fingerballet avatar fly-snail avatar freesnail123 avatar horance-liu avatar magellan-team avatar magicbowen avatar xiaolang315 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.