Browse Source

Initial commit

Add files.
master
TheoryOfNekomata 1 year ago
commit
ff1b18d808
7 changed files with 192 additions and 0 deletions
  1. +2
    -0
      .gitattributes
  2. +8
    -0
      .gitignore
  3. +6
    -0
      CMakeLists.txt
  4. +21
    -0
      LICENSE
  5. +1
    -0
      MAINTAINERS
  6. +126
    -0
      README.md
  7. +28
    -0
      bdd-for-c-mocks.h

+ 2
- 0
.gitattributes View File

@@ -0,0 +1,2 @@
# This makes the repo play nice with git on Windows
* text=auto

+ 8
- 0
.gitignore View File

@@ -0,0 +1,8 @@
cmake-build-debug
*.o
CMakeFiles/
CMakeCache.txt
Makefile
cmake_install.cmake
example
.idea/

+ 6
- 0
CMakeLists.txt View File

@@ -0,0 +1,6 @@
cmake_minimum_required(VERSION 3.24)
project(bdd_for_c_mocks)

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c99 -W -Wall")

include_directories(.)

+ 21
- 0
LICENSE View File

@@ -0,0 +1,21 @@
The MIT License (MIT)

Copyright (c) 2023 Allan Crisostomo <allan.crisostomo@outlook.com>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

+ 1
- 0
MAINTAINERS View File

@@ -0,0 +1 @@
Allan Crisostomo <allan.crisostomo@outlook.com>

+ 126
- 0
README.md View File

@@ -0,0 +1,126 @@
# bdd-for-c-mocks

## Usage

Suppose you have a library `lib` and an application `app` that uses `lib`. You want to test `app` and you want to mock
`lib`'s functions. You can do this by creating a mock header file for `lib` and including it in `app`'s test file.

### `lib.h`

This is a reference header file for `lib`. The implementation should be abstracted from us, and we want to be able to
mock its behavior.

```c
#ifndef LIB_H
#define LIB_H

int add(int a, int b);

void alert();

#endif
```

### `lib.mock.h`

We define mocked behavior for `lib`. `bdd-for-c-mocks` offers a capability to force mock behavior through modes. Note
that all mode behaviors should be implemented already.

```c
#ifndef LIB_MOCK_H
#define LIB_MOCK_H

#include <bdd-for-c-mocks.h>

mock_modes(add) {
ADD_RETURNS_SUM = 0,
ADD_RETURNS_CONSTANT_VALUE,
};

mock(add) int add(int a, int b) {
mock_mode_if(add, ADD_RETURNS_SUM) {
// suppose we want to test normal behavior
mock_return(add) a + b;
} else mock_mode_if(add, ADD_RETURNS_CONSTANT_VALUE) {
// maybe an edge case?
mock_return(add) 5;
}
}

mock(alert) void alert() {
mock_return(alert);
}

#endif
```

### `app.h`
```c
#ifndef APP_H
#define APP_H

void math();

#endif
```

### `app.c`

This is a reference implementation of `app`. We want to test the invocations done inside the `math()` function.

```c
#include "lib.h"
#include "app.h"

void math() {
int result_a = add(1, 2);
int result_b = add(3, 4);
if (result_a == result_b) {
// this is the edge case!
// alert() should be called if the add values are the same
alert();
}
}
```

### `app.test.c`

This is the test file for `app`. It includes `lib.mock.h` instead of `lib.c`. `lib.h` should still be linked because we
are sharing the original function declarations.

```c
#include <bdd-for-c.h>
#include "app.h"

spec("app") {
describe("math") {
after_each() {
mock_reset(add);
}
after_each() {
mock_reset(alert);
}
it("calls add") {
mock_mode(add, ADD_RETURNS_SUM);
mock_set_excepted_calls(math, 2);
math();
check(
mock_get_expected_calls(math) == mock_get_actual_calls(math),
"add was not called."
);
}
it("calls alert when calls from add return the same values") {
mock_mode(add, ADD_RETURNS_CONSTANT_VALUE);
math();
check(mock_is_called(alert), "alert was not called.");
}
}
}

```

+ 28
- 0
bdd-for-c-mocks.h View File

@@ -0,0 +1,28 @@
#ifndef BDD_FOR_C_MOCKS_H
#define BDD_FOR_C_MOCKS_H

#define mock_call_count_t unsigned char

#define mock(X) static mock_call_count_t actual_##X = 0;

#define mock_mode_t unsigned char

#define mock_modes(X) static mock_mode_t current_mock_mode_##X = 0; enum mock_modes_##X

#define mock_mode(X, Y) current_mock_mode_##X = Y

#define mock_mode_if(X, Y) if (current_mock_mode_##X == Y)

#define mock_return(X) actual_##X += 1; return

#define mock_reset(X) actual_##X = 0

#define mock_get_actual_calls(X) (mock_call_count_t) actual_##X

#define mock_set_expected_calls(X, Y) static const mock_call_count_t expected_##X = Y

#define mock_get_expected_calls(X) (mock_call_count_t) expected_##X

#define mock_is_called(X) mock_get_actual_calls(X) > 0

#endif

Loading…
Cancel
Save