Continuous Integration (CI)

Continuous Integration (CI)

Tests in Zig are a core concept of the language, they can be written in any file with no special syntax or framework needed like you would in C. You simply write a function which names can even be a string and use the std.testing from std in order to except all kind of needed things, from errors to primite values. You can also read the Zig Test documentation for more information.

  test "simple zig test" {
      try std.testing.expectEqual(1, 1);
  }

With httpz I was lucky to have a whole test part already implemented in the library. This allowed me to prepare different web requests and except other things than the std allow like excepting JSON or status code.

Since my project was a bit more like a rush rather than a complex complete project, I did not fully test my whole application yet.

But I did two simple tests just to test that the HTTP server is working and that a user can register.

  test "simple hello request" {
      var sqldb = try sqlite.Db.init(.{
          .mode = sqlite.Db.Mode{ .File = "mydb.db" },
          .open_flags = .{
              .write = true,
              .create = true,
          },
          .threading_mode = .Serialized, // I cant use multi thread because the HTTP server is already multi-threaded under the hood, making one thread managing multiple connections thus not allowing sqlite MultiThread
      });
      var app = App{ .db = &sqldb };
      const ctx = .{ .app = &app, .user_id = null };
      var web_test = ht.init(.{});
      defer web_test.deinit();
  
      try welcome(ctx, web_test.req, web_test.res);
      try web_test.expectStatus(200);
  }
  test "register new user" {
      // Boilerplate init begin
      var sqldb = try sqlite.Db.init(.{
          .mode = sqlite.Db.Mode{ .File = "1.db" },
          .open_flags = .{
              .write = true,
              .create = true,
          },
          .threading_mode = .Serialized,
      });
      defer sqldb.deinit();
      var c1 = try sqldb.savepoint("c1");
      var app = App{ .db = c1.db };
      const ctx = .{ .app = &app, .user_id = null };
      var web_test = ht.init(.{});
      defer web_test.deinit();
      // Boilerplate init end
  
      web_test.req.fd = try httpz.key_value.KeyValue.init(std.testing.allocator, 10);
      defer web_test.req.fd.deinit(std.testing.allocator);
      web_test.req.fd.add("username", "john");
      web_test.req.fd.add("password", "1234");
      try register(ctx, web_test.req, web_test.res); // Call to my endpoint
      try web_test.expectStatus(200);
      try web_test.expectJson(.{ .success = true });
      c1.commit();
  }

We can see that the code is very straigtforward, if the httpz library did not implement it I would have probably have had to create my own library using std.http.Client to send different kind of requests and a sort of parser to check the different responses.

In order to have my tests run automatically every time I run my application I added this line to my build.zig file:

  run_cmd.step.dependOn(&test_cmd.step);

Zig makes writing and executing tests very easy which can if wanted lead to an easy Test Driven Development workflow.