With great power comes great responsibility. The power of Lua scripting with Redis and what you can achieve using data structures beyond a key/value store is amazing. Starting out is quick and easy, and with some VIM tricks you can easily create an efficient feedback loop.
But then what, after a few iterations, how do you know that your scripts are still working?
Well, there's no reason not to include your Lua scripts in a test regime. To do this we can "simply" fire up a Redis server as a part of our tests. Now, you could argue that these are integration tests as we're using a service running out of process and perhaps that's right. But these tests run so fast, and are so specific, that I prefer to run them as a part of my development cycle. They're also executed alongside our regular unit tests, not our integration tests.
TL;DR
How does it work? We reference a redis server, start the server, execute our tests, and shutdown.
Step by step - creating our test project
- Here's our system under test, a small and rather useless script for adding a key value to the cache. ``` public class SomeServiceUsingLuaScript { private IConnectionMultiplexer _redisConnection; public SomeCacheService(IConnectionMultiplexer redisConnection) { _redisConnection = redisConnection; } public void RunsALuaScript(string value) { _redisConnection.GetDatabase().ScriptEvaluate(string.Format("redis.call('set','test','{0}')", value)); } } ```
- Create a new class library for your tests
- Use nuget and add references to Redis-64, StackExchange.Redis, xunit.core, xunit.runner.visualstudio and Shouldly ``` Install-Package Redis-64 Install-Package StackExchange.Redis Install-Package xunit.core Install-Package xunit.runner.visualstudio Install-Package Shouldly ```
- Create a folder called Redis4. As linked files, add redis-server.exe and redis.windows.conf from .\packages\redis-64.3.0.501\tools\ and set the Copy to output property to "Copy if newer"
- To make sure we start the server once for all our tests we'll use the class and collection Fixture from xunit. The class fixture will be responsible for starting the redis server, and the collection fixture will allow us to run multiple test classes with the same instance of the class fixture. For small and closely related classes I prefer to keep them in the same file. Create a new file called RedisFixture.cs with the following content
```
[CollectionDefinition("RedisCollection")]
public class RedisCollectionFixture : ICollectionFixture
{ //// Marker class for Collection Fixture } public class RedisFixture : IDisposable
{
private static Process redisProcess;public RedisFixture()
{
if (redisProcess == null || redisProcess.HasExited)
redisProcess = StartRedis();
}public void Dispose()
{
try
{
if (redisProcess != null)
{
if (redisProcess.HasExited)
return;redisProcess.StandardInput.WriteLine("SHUTDOWN"); Thread.Sleep(500); if (!redisProcess.HasExited) redisProcess.Kill(); redisProcess.Dispose(); } } catch { }
}
</li> <br/> <li>We can now write our first test using Redis
[Collection("RedisCollection")]
public class MyLuaScriptTests
{
[Fact]
public void RunALuaScriptTest()
{
var connection = ConnectionMultiplexer.Connect("localhost");
var service = new SomeCacheService(connection);service.RunsALuaScript("a"); var result = (string)connection.GetDatabase().StringGet("test"); result.ShouldBe("a"); }
}
</li> <br/> <li>The test is now available in the Visual studio test explorer (it may fail running if you're using the 2.1.0 version of the xunit.runner.visualstudio. In that case, try to downgrade it to 2.0.1). During the first test exection you may be prompted to adjust firewall settings, as Redis binds to a tcp port. </ol> You're now ready to write Lua scripts for Redis in a safe and sound manner.