Skip to content

Unit Testing

Akash Kava edited this page Nov 9, 2021 · 2 revisions

We have created NeuroSpeech.Eternity.Mocks package which provides Mock engine and storage. You can create unit tests easily as shown below.

MockEngine

MockEngine is a ServiceProvider with all mock services registered including MockStorage and MockClock.

ProcessMessageOnceAsync

It will process all messages in queue that are before MockClock's time. This will help you in increasing clock manually by the time and you can process messages again.

public class SignupWorkflow : Workflow<SignupWorkflow, string, string>
{
    public const string Resend = nameof(Resend);

    public const string Verify = nameof(Verify);

    public override async Task<string> RunAsync(string input)
    {
        var maxWait = TimeSpan.FromMinutes(15);
        var code = (this.CurrentUtc.Ticks & 0xF).ToString();
        await SendEmailAsync(input, code);
        for (int i = 0; i < 3; i++)
        {
            var (name, value) = await WaitForExternalEventsAsync(maxWait, Resend, Verify);
            switch(name)
            {
                case Verify:
                    if(value == code)
                    {
                        return "Verified";
                    }
                    break;
                case Resend:
                    await SendEmailAsync(input, code, i);
                    break;
            }
        }
        return "NotVerified";
    }

    [Activity]
    public virtual async Task<string> SendEmailAsync(
        string emailAddress, 
        string code, 
        int attempt = -1,
        [Inject] MockEmailService emailService = null) {
        await Task.Delay(100);
        emailService.Emails.Add((emailAddress, code, CurrentUtc));
        return $"{emailService.Emails.Count-1}";
    }
}

[TestClass]
public class EmailValidationTest
{

    [TestMethod]
    public async Task VerifyAsync()
    {
        var engine = new MockEngine();
        var emailService = engine.EmailService;
        var context = engine.Resolve<EternityContext>();

        // send email..
        var id = await SignupWorkflow.CreateAsync(context, "sample@gmail.com");

        engine.Clock.UtcNow += TimeSpan.FromMinutes(5);

        await context.ProcessMessagesOnceAsync();

        // check if we received the email..
        Assert.IsTrue(emailService.Emails.Any());

        var code = emailService.Emails[0].code;

        // fire event..
        await context.RaiseEventAsync(id, SignupWorkflow.Verify, code);

        engine.Clock.UtcNow += TimeSpan.FromMinutes(1);

        await context.ProcessMessagesOnceAsync();

        var status = await engine.Storage.GetWorkflowAsync(id);

        Assert.AreEqual(status.Status, ActivityStatus.Completed);

        Assert.AreEqual(status.Result, "\"Verified\"");

        Assert.AreEqual(0, engine.Storage.QueueSize);
    }

    [TestMethod]
    public async Task ResendAsync()
    {
        var engine = new MockEngine();
        var emailService = engine.EmailService;
        var context = engine.Resolve<EternityContext>();

        // send email..
        var id = await SignupWorkflow.CreateAsync(context, "sample@gmail.com");

        engine.Clock.UtcNow += TimeSpan.FromMinutes(5);

        await context.ProcessMessagesOnceAsync();

        // check if we received the email..
        Assert.IsTrue(emailService.Emails.Any());

        var code = emailService.Emails[0].code;

        // fire event..
        await context.RaiseEventAsync(id, SignupWorkflow.Resend, null);

        engine.Clock.UtcNow += TimeSpan.FromMinutes(1);

        await context.ProcessMessagesOnceAsync();

        Assert.AreEqual(2, emailService.Emails.Count);

        await context.RaiseEventAsync(id, SignupWorkflow.Verify, code);

        engine.Clock.UtcNow += TimeSpan.FromMinutes(1);

        await context.ProcessMessagesOnceAsync();

        var status = await engine.Storage.GetWorkflowAsync(id);

        Assert.AreEqual(status.Status, ActivityStatus.Completed);

        Assert.AreEqual(status.Result, "\"Verified\"");

        Assert.AreEqual(0, engine.Storage.QueueSize);

    }

    [TestMethod]
    public async Task TimedOut()
    {
        var engine = new MockEngine();
        var emailService = engine.EmailService;
        var context = engine.Resolve<EternityContext>();

        // send email..
        var id = await SignupWorkflow.CreateAsync(context, "sample@gmail.com");

        engine.Clock.UtcNow += TimeSpan.FromMinutes(5);

        await context.ProcessMessagesOnceAsync();

        // check if we received the email..
        Assert.IsTrue(emailService.Emails.Any());

        engine.Clock.UtcNow += TimeSpan.FromMinutes(20);

        await context.ProcessMessagesOnceAsync();


        engine.Clock.UtcNow += TimeSpan.FromMinutes(20);

        await context.ProcessMessagesOnceAsync();

        engine.Clock.UtcNow += TimeSpan.FromMinutes(20);

        await context.ProcessMessagesOnceAsync();

        var status = await engine.Storage.GetWorkflowAsync(id);

        Assert.AreEqual(status.Status, ActivityStatus.Completed);

        Assert.AreEqual(status.Result, "\"NotVerified\"");

        Assert.AreEqual(0, engine.Storage.QueueSize);

    }
}

In this example, we have created three unit tests, one for successful validation, one fore successful validation in 2nd attempt and one with complete timeout causing it to not verify. These unit tests are independent of storage, they are executed against mocked storage and clock.

Clone this wiki locally