How to mock standard library functions in D

I have a function that calls isFile (from std.file) on a file name and then keeps adding .1, .2, .3, etc., checking for the existence of each one.

I want to unit test a function, but for this I need a mock isFile.

I looked around a bit and I found ways to mock classes, but not with single functions.

+3


source to share


2 answers


Since my answer is slightly different from Adam, I'll add it and he can add it.

You can use "Imported Import Areas" for this purpose. See the relevant section in the documentation http://dlang.org/module.html



Here's also a working example of how you can mock a function isFile

inside a unittest block (assuming it's defined in the "mocks" module)

import std.file; 
import std.stdio;

int main(string[] args) 
{ 
    writeln(isFile("qq.d")); 
    return 0; 
} 

unittest 
{ 
    import mocks;
    writeln(isFile("qq.d")); 
}

      

+3


source


My simple solution is to mock the functions in a separate module, then use version(unittest)

to select the one you want:

version(unittest)
   import mocks.file;
else
   import std.file

void main() { isFile("foo"); } // std.file normally, mocks.file in test mode

      

Local import by Sergey Nosov works in some cases, but I think the best level is better because usually you want to test your own function:

string test_me() { isFile("qq.d"); return "do something"; }
unittest {
    assert(test_me() == "do something");
}

      

In this case, importing the scope will not work because isFile is being used too far from the test. However, version(unittest)

when imported, the point of use can override the function as needed.

Perhaps a better combination would be:

string test_me() {
    version(unittest) bool isFile(string) { return true; }
    else import std.file : isFile;
    isFile("qq.d"); return "do something";
 }

      

That is, defining the fake function locally ... but I don't like that either, now I think about it as the function does not necessarily know how it will be tested. Maybe the module mocks

that is being imported actually makes function pointers or something that can be reassigned in the unittest block .... hmm, maybe it should be a complete library, not just a collection of functions.

But I think there is a potential solution between our two answers.




Third, I want to mention, although it looks crazy, is that it is possible to globally replace a function in another module using some linker tricks:

import std.file;
import std.stdio;

// our replacement for isFile...
pragma(mangle, std.file.isFile.mangleof)
static bool isFile(string) { return true; }

int main(string[] args)
{
    writeln(isFile("qq.d")); // always does true
    return 0;
}

      

The reason the pragma (mangle) works is changing the name that the linker sees. If the linker sees two functions with the same name, one in the library and one in user code, it allows user code to override the individual library function.

So our function is used instead of lib. Important notes: the function signature must match, otherwise it will fire when you run it, and it will replace the function for your entire program, not just one place. Can be used with version (unittest).

I do not recommend using this trick, it is prone to random crashes if you make a mistake, you just want to throw it away thinking about replacing the std lib functions.

Perhaps this trick plus function pointers can be used to replace a function at runtime. The main problem with this: Since the linker completely replaces the library function with your function, you cannot use the original implementation at all!

You can also replace the entire std lib module by writing your own, giving it the same name and passing it to the compiler explicitly. I do this sometimes while working on the development of Phobos. But since this replaces all of this and represents a difference on the compiler command line, it probably doesn't help for unit tests either.

+3


source







All Articles