Code Monkey home page Code Monkey logo

cspec's Introduction

Small behavior driven development (BDD) framework for C.

Show me an example, please!

#include <stdio.h>
#include <stdbool.h>
#include <cspecs/cspec.h>

context (example) {

    describe("Hello world") {

        it("true should be true") {
            should_bool(true) be equal to(true);
        } end

        it("true shouldn't be false") {
            should_bool(true) not be equal to(false);
        } end

        it("this test will fail because 10 is not equal to 11") {
            should_int(10) be equal to(11);
        } end

        skip("this test will fail because \"Hello\" is not \"Bye\"") {
            should_string("Hello") be equal to("Bye");
        } end

    } end

}
  • Compile: gcc cspecExample.c -o cspecExample -lcspecs
  • Execute: ./cspecExample

  Hello world
    ✔ true should be true
    ✔ true shouldn't be false
    1) this test will fail because 10 is not equal to 11
    • this test will fail because "Hello" is not "Bye"


  Summary

    1) Hello world - this test will fail because 10 is not equal to 11
      - Expected <11> but was <10> [./cspecExample.c:18]

  2 success
  1 failure
  1 pending

Let's get started!

How do I install it?

  1. git clone https://github.com/mumuki/cspec.git
  2. cd cspec
  3. make install

Now, what should I do?

  • Write your C code
  • Write your specs
  • Compile with the -lshould`` option. For example: gcc -lcspecs cspecExample.c -o cspecExample
  • Run it on the console: ./cspecExample

What does cspec offer me?

It offers you a set of operations - like RSpec, Mocha or Jasmine - that allows you to make oriented behavior (unit and integration code) tests.

How do I use the framework?

context

Each behaviour to test must be declared within a context. The syntax to define a context is shown below:

context(<identifier>) {
    /* You're inside the context */
}

Inside a context, you can write functions and call them in your tests, you can also include files (.h), define macros and write scenarios using describe.

describe

Each scenario is written inside a describe, declared in this way:

describe("Brief description of the scenario") {
    /* Here goes the code */
} end

Again, inside a describe you can write functions and call them in your tests, include files (.h), define macros and write the tests using it.

it

Each it represents a test.

it("Brief description of the test") {
    /* Here goes the test code, along with the assertions */
} end

Inside it, you have to write the assertions about the behaviour you want to test. In order to do that cspec has a set of basic operations to do that, the should statements.

should

Each should is an assertion, that expects 2 values. The first is the actual value and the second, the expected one.

should_bool(<actual_boolean>) be equal to(<expected_boolean>);
should_bool(<actual_boolean>) not be equal to(<unexpected_boolean>);

should_char(<caracter_actual>) be equal to(<caracter_esperado>);
should_char(<caracter_actual>) not be equal to(<caracter_no_esperado>);

should_short(<actual_number>) be equal to(<expected_number>);
should_short(<actual_number>) not be equal to(<unexpected_number>);

should_int(<actual_number>) be equal to(<expected_number>);
should_int(<actual_number>) not be equal to(<unexpected_number>);

should_long(<actual_number>) be equal to(<expected_number>);
should_long(<actual_number>) not be equal to(<unexpected_number>);

should_float(<actual_float>) be equal to(<expected_float>);
should_float(<actual_float>) not be equal to(<unexpected_float>);

should_double(<decimal_actual>) be equal to(<decimal_esperado>);
should_double(<decimal_actual>) not be equal to(<decimal_no_esperado>);

should_ptr(<actual_pointer>) be equal to(<expected_pointer>);
should_ptr(<actual_pointer>) not be equal to(<unexpected_pointer>);

should_string(<actual_word>) be equal to(<expected_word>);
should_string(<actual_word>) not be equal to(<unexpected_word>);

Also, cspec offers syntactic sugar for some of the assertions, like the following examples:

should_bool(<actual_boolean>) be truthy;
should_bool(<actual_boolean>) not be truthy;

should_bool(<actual_boolean>) be falsey;
should_bool(<actual_boolean>) not be falsey;

should_ptr(<actual_pointer>) be null;
should_ptr(<actual_pointer>) not be null;

Hooks - before y after

Sometimes the scenarios, initial configurations, or deallocation of the variables get repeated between tests. In order to handle that, inside each describe, you can add a block code to execute before and after each test (it).

before

before {
    /* Code to execute before each test */
} end

after

after {
    /* Code to execute after each test */
} end

Note: As stated before, the context and the describe are executed secuentially, that's why it's very important to remember that the before and after must be declared in the beggining of the describe scenario, even before the first test.

Now let's see a complete example, with the execution flow

#include <stdio.h>
#include <stdlib.h>
#include <cspecs/cspec.h>

context (complete_example) {

    describe("Describe 1") {

        int *a = NULL,
             b;

        before {
            puts("before 1");
            a = malloc(sizeof(int));
            *a = 10;
            b = 20;
        } end

        after {
            puts("after 1");
            free(a);
            a = NULL;
        } end

        it("*a should be 10 and b should be 20") {
            should_int(*a) be equal to(10);
            should_int(b) be equal to(20);
        } end

        describe("Describe 2") {

            before {
                puts("before 2");
                *a = 30;
                b = 15;
            } end

            after {
                puts("after 2");
                free(a);
                a = NULL;
            } end

            it("*a should be 30 and b should be 15") {
                should_int(*a) be equal to(30);
                should_int(b) be equal to(15);
            } end

            describe("Describe 3") {

                before {
                    puts("before 3");
                    b = 150;
                } end

                after {
                    puts("after 3");
                    free(a);
                    a = NULL;
                } end

                it("*a should be 30 and b should be 150") {
                    should_int(*a) be equal to(30);
                    should_int(b) be equal to(150);
                } end

            } end

        } end

        describe("Describe 4") {

            it("*a should be 10 and b should be 20") {
                should_int(*a) be equal to(10);
                should_int(b) be equal to(20);
            } end

        } end

    } end

}

Once the code has been compiled and executed, it'll give us a report of all the tests like the following:


  Describe 1
before 1
    ✔ *a should be 10 and b should be 20
after 1
    Describe 2
before 1
before 2
      ✔ *a should be 30 and b should be 15
after 2
after 1
      Describe 3
before 1
before 2
before 3
        ✔ *a should be 30 and b should be 150
after 3
after 2
after 1
    Describe 4
before 1
      ✔ *a should be 10 and b should be 20
after 1


  Summary

  4 success

Are you using Eclipse IDE and it's showing errors all over the project?

You need to add cspec to the project dependencies. In order to do that, follow these steps...

  1. Right click on the project => Properties
  2. In the right panel go to C/C++ Build => Settings => Tool Settings => GCC C++ Linker => Libraries
  3. In the right pannel, above (Libraries (-l)), click on add and then write cspec
  4. Apply changes
  5. Recompile the project

License

This framework uses the GPLv3 as license. Fork it and contribute with the project!

Thanks!

cspec's People

Contributors

asanzo avatar bossiernesto avatar emanuelcasco avatar faloi avatar fedescarpa avatar flbulgarelli avatar jazcarate avatar luchotc avatar maximilianofelice avatar mesaglio avatar mgarciaisaia avatar mortonfox avatar raniagus 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  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  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  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

cspec's Issues

pending specs

Implement pending feature to informe that a spec or suite of specs aren't currently passing, but shouldn't make the test fail.

It would be great if it could replace both describe and it, as in rspec.

conflicting types for 'Function' when using with `-lreadline`

If I try to compile this code using -lreadline and -lcspecs:

#include <readline/readline.h>
#include <cspecs/cspec.h>

It crashes, because both headers have definitions for Function type:

/usr/include/readline/rltypedefs.h:35:13: error: conflicting types for ‘Function’
   35 | typedef int Function () __attribute__ ((deprecated));
      |             ^~~~~~~~
In file included from src/example.c:3:
/usr/include/cspecs/cspec.h:17:23: note: previous declaration of ‘Function’ was here
   17 |         typedef void(*Function)(void);
      |                       ^~~~~~~~

Assertions for arrays

It would be nice to have array equality assertions, comparing element by element.

Some thoughts about this:

  • As seen here, C++ supports literal arrays, but I believe it's mandatory to assign them to a variable: int expected[] = { 16, 2, 77, 40, 12071 };. It would be great to avoid the variable and write something like should(numbers) be equal({ 16, 2, 77, 40, 12071 })
  • The user should be able to decide whether the elements must be in the same order or not

Idea: Syntax change

Most C/C++ code formatters (like clang-format) are not able to understand the {} end syntax, so they end up formatting CSpec code like this:

#include <cspecs/cspec.h>
#include <stdbool.h>
#include <stdio.h>

context(example) {

    describe("Hello world"){

        it("true should be true"){should_bool(true) be equal to(true);
}
end

        it("true shouldn't be false") {
    should_bool(true) not be equal to(false);
}
end

        it("this test will fail because 10 is not equal to 11") {
    should_int(10) be equal to(11);
}
end

        skip("this test will fail because \"Hello\" is not \"Bye\"") {
    should_string("Hello") be equal to("Bye");
}
end
}
end
}

In a custom branch I've tried getting rid of end macro and moving the curly braces inside parenthesis:

#include <cspecs/cspec.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

context(example) {

    describe("Hello world", {
        char *hello;

        before({
            hello = strdup("Hello");
        });
    
        after({
            free(hello);
        });

        it("true should be true", {
            bool the_truth = true;
            should_bool(the_truth) be equal to(true);
        });

        it("true shouldn't be false", {
            bool the_truth = true;
            should_bool(the_truth) not be equal to(false);
        });

        it("this test will fail because 10 is not equal to 11", {
            int ten = 10;
            should_int(ten) be equal to(11);
        });

        skip("this test will fail because \"Hello\" is not \"Bye\"", {
            should_string(hello) be equal to("Bye");
        });
    });
}

And it indents almost as expected (it doesn't work for single-line blocks, even though I've set every AllowShort*OnASingleLine option to false/Never/None):

#include <cspecs/cspec.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

context(example) {

    describe("Hello world", {
        char *hello;

        before({ hello = strdup("Hello"); });
    
        after({ free(hello); });

        it("true should be true", {
            bool the_truth = true;
            should_bool(the_truth) be equal to(true);
        });

        it("true shouldn't be false", {
            bool the_truth = true;
            should_bool(the_truth) not be equal to(false);
        });

        it("this test will fail because 10 is not equal to 11", {
            int ten = 10;
            should_int(ten) be equal to(11);
        });

        skip("this test will fail because \"Hello\" is not \"Bye\"", { should_string(hello) be equal to("Bye"); });
    });
}

But, I find this syntax much more easier to understand and use, and also more beginner friendly. What do you think?

Permission error for using lib at compiling.

After installing the lib with:

make
sudo make install

when compiling with:

gcc test.c -o test -lcspecs

I was having this permissions issue:
image

The solution was change permissions for cspecs folder and the lib:

sudo chmod o+r,o+x /usr/include/cspecs/
sudo chmod o+r /usr/include/cspecs/cspec.h 
sudo chmod o+r,o+x /usr/lib/libcspecs.so 

image

I don't know if it only happened to me or someone else.

Can't include CSpec as an external CMake project

CMake allows to include an external project with Git adding these lines:

 set(EXTERNAL_PROJECT_DIR ${CMAKE_BINARY_DIR}/external) 
  
 include(ExternalProject) 
 ExternalProject_Add(cspecs 
     GIT_REPOSITORY https://github.com/mumuki/cspec 
     CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${EXTERNAL_PROJECT_DIR} 
 ) 
  
 include_directories(${EXTERNAL_PROJECT_DIR}/include) 
 link_directories(${EXTERNAL_PROJECT_DIR}/lib)

 link_libraries(cspecs)

But it fails because the installation doesn't include CSpec's header:

[  6%] Creating directories for 'cspecs'
[ 12%] Performing download step (git clone) for 'cspecs'
-- Avoiding repeated git clone, stamp file is up to date: '/home/aranieri/so-commons-library-install-from-zip/tests/unit-tests/build/cspecs-prefix/src/cspecs-stamp/cspecs-gitclone-lastrun.txt'
[ 18%] Performing update step for 'cspecs'
HEAD is now at 80569c0 Merge pull request #29 from mumuki/shrink-image
[ 25%] No patch step for 'cspecs'
[ 31%] Performing configure step for 'cspecs'
-- Configuring done
-- Generating done
-- Build files have been written to: /home/aranieri/so-commons-library-install-from-zip/tests/unit-tests/build/cspecs-prefix/src/cspecs-build
[ 37%] Performing build step for 'cspecs'
make[5]: Entering directory '/home/aranieri/so-commons-library-install-from-zip/tests/unit-tests/build/cspecs-prefix/src/cspecs-build'
make[6]: Entering directory '/home/aranieri/so-commons-library-install-from-zip/tests/unit-tests/build/cspecs-prefix/src/cspecs-build'
make[7]: Entering directory '/home/aranieri/so-commons-library-install-from-zip/tests/unit-tests/build/cspecs-prefix/src/cspecs-build'
Consolidate compiler generated dependencies of target cspecs
make[7]: Leaving directory '/home/aranieri/so-commons-library-install-from-zip/tests/unit-tests/build/cspecs-prefix/src/cspecs-build'
[100%] Built target cspecs
make[6]: Leaving directory '/home/aranieri/so-commons-library-install-from-zip/tests/unit-tests/build/cspecs-prefix/src/cspecs-build'
make[5]: Leaving directory '/home/aranieri/so-commons-library-install-from-zip/tests/unit-tests/build/cspecs-prefix/src/cspecs-build'
[ 43%] Performing install step for 'cspecs'
make[5]: Entering directory '/home/aranieri/so-commons-library-install-from-zip/tests/unit-tests/build/cspecs-prefix/src/cspecs-build'
make[6]: Entering directory '/home/aranieri/so-commons-library-install-from-zip/tests/unit-tests/build/cspecs-prefix/src/cspecs-build'
make[7]: Entering directory '/home/aranieri/so-commons-library-install-from-zip/tests/unit-tests/build/cspecs-prefix/src/cspecs-build'
make[7]: Leaving directory '/home/aranieri/so-commons-library-install-from-zip/tests/unit-tests/build/cspecs-prefix/src/cspecs-build'
[100%] Built target cspecs
make[6]: Leaving directory '/home/aranieri/so-commons-library-install-from-zip/tests/unit-tests/build/cspecs-prefix/src/cspecs-build'
Install the project...
-- Install configuration: ""
-- Up-to-date: /home/aranieri/so-commons-library-install-from-zip/tests/unit-tests/build/external/lib/libcspecs.so
make[5]: Leaving directory '/home/aranieri/so-commons-library-install-from-zip/tests/unit-tests/build/cspecs-prefix/src/cspecs-build'
[ 50%] Completed 'cspecs'
make[4]: Leaving directory '/home/aranieri/so-commons-library-install-from-zip/tests/unit-tests/build'
[ 50%] Built target cspecs
make[4]: Entering directory '/home/aranieri/so-commons-library-install-from-zip/tests/unit-tests/build'
make[4]: Leaving directory '/home/aranieri/so-commons-library-install-from-zip/tests/unit-tests/build'
make[4]: Entering directory '/home/aranieri/so-commons-library-install-from-zip/tests/unit-tests/build'
[ 56%] Building C object CMakeFiles/commons-unit-test.dir/test_bitarray.c.o
/home/aranieri/so-commons-library-install-from-zip/tests/unit-tests/test_bitarray.c:20:10: fatal error: cspecs/cspec.h: No such file or directory
   20 | #include <cspecs/cspec.h>
      |          ^~~~~~~~~~~~~~~~

Publish cspec tests

I've heard that cspecs actually has tests - in cspec ;) - but they are not here nor in the organization repo's. Please publish them!

Error Squiggle when copy-pasting example code into Visual Studio Code

Error

Upon clean install of cspec, following the explanation in the README.md file, and including the required header files for the example file copied from the previous file, Visual Studio Code (with C/C++ extensions installed) detects the error:

argument of type "void" is incompatible with parameter of type "Runnable"C/C++(167)

Though this error is only aesthetic, as the file compiles correctly and the tests run as expected.

Steps to reproduce it

  • Clean install of cspec
  • Copy full example off README.md into a .c file.
  • Check Intellisense's error

Things attempted to fix the error:

  • Copy the replaced text (__describe("Hello world", ({ void __$__()) into the file.
  • Change Intellisense's version of the compiler
  • Switched from GCC to CLang and back
  • Tested in different computers

Attached a screenshot of the error in question, the second error comes from not detecting the first one as a correct structure, so it doesn't matter for the initial issue.

2023-04-05-19:56:45-screenshot

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.