Skip to content

Testing low level API

Daniel Frantík edited this page May 13, 2026 · 1 revision

Testing with TikFakeConnection — Low-level API

⚠️ Alpha APItik4net.testing is in early development. The builder methods and class names may change in future releases.

Package: tik4net.testing (NuGet) — add it to your test project only.
See also: Testing mid-level API · Testing high-level API

TikFakeConnection intercepts at CallCommandSync — the lowest point in the call stack — so every higher layer runs through real code. At this level you work directly with raw MikroTik sentence arrays and inspect command rows verbatim.

Setup

using tik4net;
using tik4net.Testing;

Add tik4net.testing as a NuGet/project reference in your test project. No router or network connection is required.

Building entities with private fields: when you need to set read-only entity properties (.id, dynamic, …) use WithId / WithValue from TikFakeEntityExtensions — see Testing high-level API — Building fake entity instances.


Registering raw sentence responses

Use WithResponse(predicate, sentences) to register a handler. The first matching handler wins (registration order).

var conn = new TikFakeConnection()
    .WithResponse(
        predicate: rows => rows.First() == "/interface/print",
        sentences: new ITikSentence[]
        {
            new TikFakeReSentence(new Dictionary<string, string>
            {
                { ".id",      "*1"     },
                { "name",     "ether1" },
                { "type",     "ether"  },
                { "running",  "true"   },
                { "disabled", "false"  },
            }),
            new TikFakeReSentence(new Dictionary<string, string>
            {
                { ".id",      "*2"     },
                { "name",     "ether2" },
                { "type",     "ether"  },
                { "running",  "false"  },
                { "disabled", "false"  },
            }),
            new TikFakeDoneSentence(),
        });

// Execute the code under test
string[] command = new string[] { "/interface/print" };
IEnumerable<ITikSentence> result = conn.CallCommandSync(command);

// Assert
var reRows = result.OfType<ITikReSentence>().ToList();
Assert.AreEqual(2, reRows.Count);
Assert.AreEqual("ether1", reRows[0].GetResponseField("name"));

Dynamic response factory

When the response should depend on the actual command rows (e.g. to echo back a filter value), use the overload that accepts Func<IEnumerable<string>, IEnumerable<ITikSentence>>:

var conn = new TikFakeConnection()
    .WithResponse(
        predicate: rows => rows.First() == "/ip/address/print",
        response:  rows =>
        {
            // rows may contain filter parameters like "?.id=*1"
            bool filtered = rows.Any(r => r.Contains(".id=*1"));
            var result = new List<ITikSentence>();
            if (!filtered || rows.Any(r => r == "?.id=*1"))
                result.Add(new TikFakeReSentence(new Dictionary<string, string>
                    { { ".id", "*1" }, { "address", "10.0.0.1/24" } }));
            result.Add(new TikFakeDoneSentence());
            return result;
        });

Simulating errors (!trap)

var conn = new TikFakeConnection()
    .WithTrap(
        predicate: rows => rows.First() == "/ip/address/add",
        message:   "already have such item");

// CallCommandSync returns the trap sentence; Execute* methods throw TikCommandTrapException
IEnumerable<ITikSentence> result = conn.CallCommandSync("/ip/address/add", "=address=10.0.0.1/24");
Assert.IsTrue(result.OfType<ITikTrapSentence>().Any());

Verifying what was sent

SentCommands records every command row array in order.

conn.AssertWasSent("/interface/print");

// Fine-grained: check that a specific parameter was included
conn.AssertWasSent(rows => rows.Any(r => r == "=name=ether1"));

// Count calls (e.g. ensure /set was called exactly once)
Assert.AreEqual(1, conn.GetCallCount("/ip/address/set"));

Catch-all handler

Register a catch-all last to avoid InvalidOperationException on unexpected commands:

var conn = new TikFakeConnection()
    .WithResponse(rows => rows.First() == "/system/identity/print",
                  new ITikSentence[] { new TikFakeReSentence(...), new TikFakeDoneSentence() })
    // catch-all: every other command succeeds silently
    .WithNonQuery(rows => true);

Clone this wiki locally