Unit test

how do you test requests to external services in tests? how to make mock ?

the common way is, to use a SandBox’ed Environment or a Virtual Machine like Oracle VirtualBox or Microsoft’s Virtual PC.

I do not quite understand. why do I need a virtual machine? How can I use it for a non-my external service? I’m interested in how you make a mock

To test Software, that you don’t trust, or if you take care about Viruses, Trojan’s or other Malware that could damage your existing System, I advise you to use a SandBox.
A SandBox is like a play ground Environment for a special Session - provide Operating System Facilities, but dont change or operate between the Software and Operating System directly.
For a SandBox, you don’t need Install a second copy of Windows - it comes all inclusive.
But it is limited on some Resources.

A Virtual Machine is - like the Name says: virtual.
Here, you can make anything what you would do.

You can emulate Hardware for your Test’s - like virtual Hard Disk Drives - which comes as a
Image File (and you dont need a native Drive, to format the Drive with a Folder Structure.
Examples can be Microsoft NTFS, or FAT32 File System Format’s)

Or, you can emulate Network Card’s (NIC’s), etc. …
There are no known Limits - okay, sure: The Space (RAM, and DiskDrive, and Computer Speed) …

Examples for Virtual Machines Providers are Microsoft VirtualPC, ORACLE Virtual Box, or VMWare.
On VMWare, take care:

  • there you could use older Version of the Product’s, beecause new Version’s consume a huge
    amount of Ressource’s

If you plan to virtualize a Operating System, to run paralell to your current Windows Session, make sure, you have VM-vt Features enabled in your PC-BIOS.
I can not give Support in this, because each Vendor use other Options, and Menu System - take a look to the Search Engine of your Trust.

But, back to the red line …
VM-Box, and SandBox’s are usefull things in Development Stage.
Because, if you damage the (virtual) System, you can quickly copy a Backup File, and start your
tries again with fresh Environment.

Thanks for reading
paule32

my question is not about that at all. You have a post request to an external service. You know which answer is correct and which is wrong, and you need to test your method, you cannot rely on an external service in tests, you need to somehow bypass the call to an external service in the test. For example this library for Erlang GitHub - eproxus/meck: A mocking library for Erlang

1 Like

There is nothing out of the box. There are lots of options though. For example, implement a mock version of the library that calls the external service and load it instead of the real thing by changing the (library) search path. You can also use wrap_predicate/4 to create a wrapper around any predicate. That is intended to call the original, but of course you don’t need to. You can even abolish/1 the old predicate and assert a new definition.

None of that is portable. Wrapping predicates is really SWI-Prolog specific. There is no portability in search path handling (but many systems have it) and ISO turned abolish/1 into something useless because you can only use it on dynamic code where, in addition to retractall/1, it removes the predicate properties (dynamic, etc.) SWI-Prolog kept the pre-ISO semantics where abolish/1 applies to any predicate.

Than you need an alternative implementation for the external server. I don’t know how much you can generalize that. I guess it can be anything.

2 Likes

I agreed - it can anything.

What you could use is a Proxy-Server, that can block by regex.
Else, I would use WireShark, to see, what goes over the line.

no, all this is not needed, you just need to replace the function for the duration of the test.
In simple words, in mock testing, we replace the dependent objects with mock objects. These mock objects simulate or ‘mock’ the behavior of real objects and exhibit the exact characteristics of the authentic ones. The main goal of mock testing is to centralize the focus on the testing without being concerned about the dependencies.

Let’s take an example- You have a web service that makes a POST request to

update or store new entries in the API server every time you hit submit on a form.

We do not need to store unnecessary test data in the server each time we run a

test. Mock testing allows the user to replace the service with a mock object to validate the functionality without storing redundant information.

wrap_predicate/4 can do that. After the test you simply use unwrap_predicate/2 to restore the old behavior. This is used by trace/1,2 as well as tabling.

So, you get something like this:

?- wrap_predicate(http_client:http_get(URL, Data, Options), test,
                  _Wrapped, mock_http_get(URL, Data, Options)).
<run tests>
?- unwrap_predicate(http_client:http_get/3, test).
5 Likes

I don’t need all calls to mock , but now I got that everything has changed.

or it is possible to install and cancel for each test?

Isn’t that what the unwrap_predicate/2 call does? It removes a named wrapper. If you want examples, look at the implementation of trace/2, which prints the passes through the “ports” (call, exit, fail, redo).

thanks, I already figured it out.

Jan: if the test fails in the middle, will unwrap_predicate fail to run? It sounds more like a try/finally situation, and by then, the setup becomes tedious. Is there a way to override a predicate for the duration of the test with automatic unwrapping regardless of the test’s outcome?

See setup_call_cleanup/3, where you do the wrap in the setup, the test in the call and the unwrap in the cleanup phase of this predicate. This predicate

  • calls the setup as once/1. On anything but success, we are done. I.e., if the setup fails, the call fails and if it throws throws. Once we get through the setup, it is ensured that the cleanup is called, even if a resource error or interrupt happens between the setup and call.
  • call the call phase. Now, the cleanup is used as finalize step. Completion state (fail, succeed, throw) is that of the call phase, unless the cleanup throws a “more urgent” error. For example, if call raises some normal error and cleanup gets an abort, execution is still aborted.

Can it be done once for all the tests in a module, or in a way that it is not that intrusive?

Shouldn’t the tests be hermetic automatically, with the test runner isolating their side effects from each other? Then we could just assert things in a test and not have to retract them with a setup_call_cleanup/3.

You can add setup(Goal) and cleanup(Goal) to the begin_tests/2 as well as each individual test. Also there we guarantee that the cleanup is called.

SWI-Prolog (or any Prolog I know) doesn’t really provide fully side-effect-free isolation. We could consider running each test using snapshot/1, which would deal with assert/retract. That might be a good idea. Alternatively we could run each test in a thread and use thread_local/1 rather than dynamic/1. That isolates a little more as also flags and global variables are thread specific. But, not everything runs completely unmodified in a thread (99% does). The safe solution would be to fork, but that is relatively slow and does not work on Windows.