GithubHelp home page GithubHelp logo

funsolator's Introduction

Funsolator: the Function Isolator

Some times you happen to work on some code where it not easy to unit test a function.

It could be part of a large file where dependencies makes is impossible/hard to compile it in isolation. You may not want to use a sprout-method or in other ways change the target code to be testable, nonetheless you really want to unit test this one function with weird logic that your new shiny code depends on.

What if you could just have that function isolated so you could test it…

At times I have wondered where does such a tool exist? I have not found it. The Mocking frameworks I have seen they seem to prefer a nice clean interface or at least the ability to compiling a production version the file where the code you want under test is.

Introducing “The funsolator”

The goal is to have an easy and brutish way to extract a method, such that it may be tested in isolation.

The basic idea is to extract the source-code into a generated file, that can be used by the tests. Doing so in a manner such that any changes to the production function is picked up by the build system.

This relies on a primitive python-script and some CMake magic.

Example

Imagine having the file ./production_code/gnarly_beast.cpp where you have an interesting function

#include "the_whole_world"

/// #ifdef THIS_AND_THAT

/// millions of omitted lines of code

int interesting_fun(CustomType bar) { return bar.complex_calculation() + 1; }

You are not allowed to change the production code but on build time you could generate a file from a ./test_code/beast.template

struct CustomType {
  int injected_result;
  int complex_calculation() { return injected_result; }
};

FUNSOLATE("int interesting_fun(CustomType bar)")

So in your tests ./test_code/test_beast.cpp you could write

#include <tamed/beast.h>

#include <stdlib.h>

int main(int argc, char *argv[]) {
  CustomType bar;

  bar.injected_result = 3;

  if (interesting_fun(bar) == 4) {
    return EXIT_SUCCESS;
  } else {
    return EXIT_FAILURE;
  }
}

The test would be configured in your ./test_code/CMakeLists.txt with

set(beast "${CMAKE_CURRENT_SOURCE_DIR}/../production_code/gnarly_beast.cpp")

add_custom_command(
  OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/tamed/beast.cpp ${CMAKE_CURRENT_SOURCE_DIR}/tamed/beast.h
  COMMAND  python3 ${CMAKE_CURRENT_SOURCE_DIR}/funsolator.py ${beast} ${CMAKE_CURRENT_SOURCE_DIR}/beast.template 
  DEPENDS ${beast} ${CMAKE_CURRENT_SOURCE_DIR}/beast.template
  VERBATIM
)

Now any change to gnarly_beast.cpp would generate a new tamed/beast.{cpp|h} pair and you could gain some confidence that the function behaves as expected - and continues to.

And changes to your beast.template with the definition/inclusion of needed types would also result in updates

But OMG why not refactor the production code?

  • Sometimes you do not maintain the code you depend on. Think a third party library.
  • The code maybe to gnarly to and you need something to start prying it open.
  • You just want to see how to code works.

Often times, especially with C++/C code, you can instruct the compiler to compile a single file and just keep on adding stub implementations until the compilation succeeds. This can be tedious, almost impossible and a waste of time as you end up needing to maintain the stubs.

The Funsolator could be a tool to make this less tedious. With less friction it lowers the entry barrier for adding tests & encourages more granular tests of the gnarly_beasts.cpp out there.

Improvements

This is just a toy example - to see if it was possible to do this.

It turns out it was not just pulling out the function, you also want a header to include, the correct incantations of CMake, ….

This generated output was added under the source dir tamed/beast.h, as it will allow your IDE to pick up the files.

Any nifty things to add could include:

  • Better cmake functions to add the test
    • Could it filter on extension, if its .template use the funsolator (which would probably need to define its “target file” / the beast
    • Turn into a CMake module to hide the functions and scripts
  • Try to create an example using Makefiles ?
  • Use a real test framework for the example

How to try out the funsolator

~/Projects/funsolator$ cmake -G Ninja -B build .
~/Projects/funsolator$ ninja -C build
~/Projects/funsolator$ ninja -C build test

funsolator's People

Watchers

 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.