GithubHelp home page GithubHelp logo

testcontainers / testcontainers-c Goto Github PK

View Code? Open in Web Editor NEW
14.0 3.0 1.0 254 KB

Testcontainers for C and, soon, C++, built on the top of Testcontainers for Go. Includes a WireMock module for test/demo purposes

License: MIT License

Go 49.08% C 28.45% CMake 22.47%
c cpp testcontainers wiremock hacktoberfest testcontainers-custom-container

testcontainers-c's Introduction

Testcontainers for C/C++ and other native languages

Testcontainers for C/C++ Logo

Slack: testcontainers-c on slacktestcontainers.org Stability: Experimental GitHub release (latest by date)

WARNING: This is a prototype. There is a lot to do before it can be distributed and used in production, see the GitHub Issues. The plan is to provide vcpkg and Conan packages; now it is an importable CMake project. A feasible level of feature parity with Testcontainers Go is needed too, hence a lot of wrapper coding.

This is not a standalone Testcontainers engine, but a C-style shared library adapter for native languages like C/C++, D, Lua, Swift, etc. It is an MVP SDK that can be later extended for the languages. The project is based on Testcontainers for Go which is one of the most powerful Testcontainers implementations.

Contributions and feedback are welcome! Also join the #testcontainers-c channel on the Testcontainers Slack.

Key Features

  • Minimum viable Testcontainers API functionality: starting and terminating containers, passing files, exposing ports, accessing container logs, etc.
  • Minimum HTTP client wrapper to simplify requests and assertions
  • Testcontainers for Go under the hood
  • Wrappers for native C types to minimize Golang conversion code on the user side
  • Support for C and C++ projects. A fancy C++ wrapper is coming soon
  • Support for Modules, e.g. the WireMock module

Quick Start

In this section, we will build a demo C application that uses Testcontainers C/C++ for deploying a WireMock API server, sends a simple HTTP request to this service, and verifies the response. We will not be using any C/C++ test framework for that.

For a test framework framework example, see the Google Test sample project.

Build the project

Right now you have to check out and build the project to use it locally. You will need CMake, Docker, Golang 1.19++, and recent C/C++ build tools. The first build may take a while because the build process will need to download Testcontainers for Go and its dependencies like Docker client libraries, and then repackage it as a shared library using go build -buildmode=c-shared.

cmake .
cmake --build .
ctest --output-on-failure

Disclaimer: The commands above may explode, proper coverage on different platforms is yet to be implemented. At the moment the default compiler and linker options are used, so code is not very portable. CMake's install option is not being tested at all, stay tuned for package managers. Any patches and issue reports are welcome!

Writing a first test

You can use a C/C++ framework for writing tests, e.g. CTest or CppUnit. Or you can just have a small launcher as presented below. Below there is a code of the WireMock demo that only uses the library but not a specialized WireMock module (see below).

Note that in this example, we do not terminate the container, because Testcontainers for Go injects Moby Ryuk sidecar container by default to automatically terminate the instance. We also do not worry about memory leaks too much, because the process will exit anyway.

main.c

main.c - Show me the Code
#include <stdio.h>
#include <string.h>
#include "testcontainers-c.h"

#define DEFAULT_IMAGE "wiremock/wiremock:3.0.1-1"

int main() {
    printf("Using WireMock with the Testcontainers C binding:\n");

    printf("Creating new container: %s\n", DEFAULT_IMAGE);
    int requestId = tc_new_container_request(DEFAULT_IMAGE);
    tc_with_exposed_tcp_port(requestId, 8080);
    tc_with_wait_for_http(requestId, 8080, "/__admin/mappings");
    tc_with_file(requestId, "test_data/hello.json", "/home/wiremock/mappings/hello.json");
    struct tc_run_container_return ret = tc_run_container(requestId);
    int containerId = ret.r0;
    if (!ret.r1) {
        printf("Failed to run the container: %s\n", ret.r2);
        return -1;
    }

    printf("Sending HTTP request to the container\n");
    struct tc_send_http_get_return response = tc_send_http_get(containerId, 8080, "/hello");
    if (response.r0 == -1) {
        printf("Failed to send HTTP request: %s\n", response.r2);
        return -1;
    }
    if (response.r0 != 200) {
        printf("Received wrong response code: %d instead of %d\n%s\n%s\n", response.r0, 200, response.r1, response.r2);
        return -1;
    }
    printf("Server Response: HTTP-%d\n%s\n\n", response.r0, response.r1);
    return 0;
}

Sample output

This is what a very simple run without a test framework may look like.

Sample Output

Installing the library

It is advised to include CMake as a dependent module for now. If you like living dangerously until proper vcpkg and Conan packages are ready, you can optionally install the library to your system:

# NOT RECOMMENDED
cmake --install ..

Documentation

Coming soon: guidelines, specs and code documentation. Check out the examples for now.

Usage in C/C++

See the examples and demos for more examples.

Usage in other languages

TL;DR: You get the C header file, a shared library object or a DLL file from the Testcontainers C module, Then, you know the drill. Feel free to contribute examples or SDKs for the languages!

Modules

As for other Testcontainers implementations, Testcontainers for C/C++ allow writing extensions that extend the SDK and APIs to make usage of a particular service provider easier. The expectation is that the modules are implemented in a separate dynamic library and linked to the consumer project.

Available modules

  • Generic container for DYI containers (embedded)
  • WireMock - for API mocking and integration testing

Why modules?

Modules help to simplify test development and maintenance by encapsulating domain-specific logic of a target container. For example, the WireMock module adds an API to simplify the configuration of the container. You can also use modules to create specific asserts for the container, or even attach full-fledged API clients for fine-grain testing.

Initializing WireMock with the module:

#include "testcontainers-c-wiremock.h"

int main() {
    printf("Creating new container: %s\n", DEFAULT_WIREMOCK_IMAGE);
    int requestId = tc_wm_new_default_container();
    tc_wm_with_mapping(requestId, "test_data/hello.json", "hello");
    struct tc_run_container_return ret = tc_run_container(requestId);

    // ...
}

The same initialization without a module (using the "Generic Container" API):

Show me the Code
#include "testcontainers-c.h"

#define DEFAULT_IMAGE "wiremock/wiremock:3.1.0-1"

int main() {
    printf("Using WireMock with the Testcontainers C binding:\n");

    printf("Creating new container: %s\n", DEFAULT_IMAGE);
    int requestId = tc_new_container_request(DEFAULT_IMAGE);
    tc_with_exposed_tcp_port(requestId, 8080);
    tc_with_wait_for_http(requestId, 8080, "/__admin/mappings");
    tc_with_file(requestId, "test_data/hello.json", "/home/wiremock/mappings/hello.json");
    struct tc_run_container_return ret = tc_run_container(requestId);

    // ...
}

Creating new modules

You are welcome to contribute more modules in this or a standalone repository!

NOTE: Some modules are stored in this repository for demo and prototyping purposes. If you develop new modules, once vcpkg or Conan packaging is implemented for Testcontainers C, you might want to develop your module in a standalone repository instead.

Credits

Using a complex Golang framework from C/C++ is not trivial. Neither the CMake files are. This project would not succeed without many quality articles and help from the community.

Kudos to:

Read More

testcontainers-c's People

Contributors

grooverdan avatar oleg-nenashev avatar simonverhoeven avatar

Stargazers

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

Watchers

 avatar  avatar  avatar

Forkers

simonverhoeven

testcontainers-c's Issues

Conan packaging

It would be nice to publish the Testcontainers packages to Conan

  • Add package and build definitions
  • Register the package on vcpkg
  • Add publishing to GitHub Actions

References

Add standalone CUnit Example

Currently we have a CTest test suite inside the code, and it is a good start for tests. At the same time, it would be nice to have another example that uses the code as an external package in the examples

Inject informative field names into C Structures

When using CGO, structures like this one are generated:

/* Return type for RunContainer */
struct RunContainer_return {
	GoInt r0; /* id */
	char* r1; /* errstr */
};
extern struct RunContainer_return RunContainer(GoInt requestID);

/* Return type for GetURI */
struct GetURI_return {
	GoString r0; /* uri */
	GoInterface r1; /* e */
};

Maybe CGO could be tweaked or configured to inject proper field names to improve DevX

Wishlist

This is how we want to see the code once everything is done, maybe:

#include <stdio.h>
#include <string.h>
#include "testcontainers-c.h"
#include "testcontainers-c-wiremock.h"

#define DEFAULT_IMAGE "wiremock/wiremock:3.1.0-1"

int main() {
    printf("Using WireMock with the Testcontainers C binding:\n");

    printf("Creating new container: %s\n", DEFAULT_IMAGE);
    int requestId = NewContainerRequest(DEFAULT_IMAGE);
    WithExposedTcpPort(requestId, 8080);
    WithWaitForHttp(requestId, 8080, "/__admin/mappings");
    WithFile(requestId, "test_data/hello.json", "/home/wiremock/mappings/hello.json");
    struct TestContainer container = RunContainer(requestId);
    if (container.Id == -1) {
        printf("Failed to run the container: %s\n", container.errorMsg);
        return -1;
    }

    printf("Sending HTTP request to the container\n");
    struct HttpGetResponse response = SendHttpGet(container.Id, 8080, "/hello");
    if (response.code == -1) {
        printf("Failed to send HTTP request: %s\n", response.errorMsg);
        return -1;
    }
    if (response.code != 200) {
        printf("Request failed: HTTP-%d\n%s\n", response.code, response.errorMsg);
        return -1;
    }
    printf("Server Response: HTTP-%d\n%s\n\n", response.code, response.msg);
    return 0;
}

Initial Documentation structure

It would be nice to initialize the documentation structure, published to GitHub Pages

  • Overview
  • Quickstart
  • Feature docs
  • Placeholder for Doxygen-generated code docs

vcpkg packaging

It would be nice to publish the Testcontainers packages to vcpkg

  • Add package and build definitions
  • Register the package on vcpkg
  • Add publishing to GitHub Actions

References

Switch API to use native C types

Currently a lot of GString / GInt are used. It would be nice to switch to basic types so that API is easier to use. Golang code is highly customized anyway

Add C++ binding library

The current C API is quite complicated for consumption. It would be great to have a C++ binding with

  • Namespace
  • Proper Structures
  • TBD: Classes

Add CppUnit demo

It would be great to have a CppUnit Demo showcasing integration of the library, with automatic teardown of the container

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.