2

I want a unit test that verifies 2 function calls happen in the correct order. In the example, the first function encrypts a file and saves it to the file system, and the second function sends the encrypted file to a 3rd party processor (via FTP).

I am using NSubstitute as the mock framework and FluentAssertions to aid in test verification. It does not seem like this is something you can achieve with NSubstitute out of the box.

public void SendUploadToProcessor(Stream stream, string filename)
{
    var encryptedFilename = FilenameBuilder.BuildEncryptedFilename(filename);
    FileEncrypter.Encrypt(stream, filename, encryptedFilename);
    FileTransferProxy.SendUpload(encryptedFilename);
}

[TestMethod, TestCategory("BVT")]
public void TheEncryptedFileIsSent()
{
    var stream = new MemoryStream();
    var filename = Fixture.Create<string>();

    var encryptedFilename = Fixture.Create<string>();
    FilenameBuilder
        .BuildEncryptedFilename(Arg.Any<string>())
        .Returns(encryptedFilename);

    Sut.SendUploadToProcessor(stream, filename);

    // Something here to verify FileEncrypter.Encrypt() gets called first

    FileTransferProxy
        .Received()
        .SendUpload(encryptedFilename);
}
Matt Slavicek
  • 841
  • 10
  • 18

2 Answers2

4

Try Received.InOrder in the NSubstitute.Experimental namespace.

Something like this (I haven't tested this):

Received.InOrder(() => {
  FileEncrypter.Encrypt(stream, filename, encryptedFilename);
  FileTransferProxy.SendUpload(encryptedFilename);
});

If you're not comfortable relying on experimental functionality, you will need to set up callbacks to store calls in order, then assert on that.

var calls = new List<string>(); //or an enum for different calls
FileEncrypter.When(x => x.Encrypt(stream, filename, encryptedFilename))
             .Do(x => calls.Add("encrypt"));
FileTransferProxy.When(x => x.SendUpload(encryptedFilename))
                 .Do(x => calls.Add("upload"));
// Act

// Assert calls contains "encrypt","upload" in the correct order

If you do end up trying Received.InOrder, please leave some feedback on the discussion group. If we get some feedback about it working well for others then we can promote it to the core namespace.

David Tchepak
  • 9,826
  • 2
  • 56
  • 68
  • 1
    Thanks David. I had forgotten about the When-Do construct, that's what I was looking for. The NSubstitute.Experimental namespace is not available in the NuGet package, how would I get a version of NSubstitute that does include that namespace? – Matt Slavicek Apr 19 '13 at 15:14
  • The NuGet version has it (it isn't a separate DLL, just use `using NSubstitute.Experimental`). It was introduced with v1.5.0 (current version is v1.6.0). – David Tchepak Apr 19 '13 at 23:32
  • 1
    I tried putting in the using statement initially, and it wasn't recognized for some reason. It is working now. I left some feedback on the discussion group. Thanks! – Matt Slavicek Apr 22 '13 at 13:59
1

Although it is not an answer persee, but verifying the explicit order as part of a unit test is bad practice. You should never test the implementation details. Just make sure the input is properly converted to the output and add some alternative scenarios that basically proof the expected behavior. That's the precise reason why this functionality was deprecated in RhinoMocks and that FakeItEasy doesn't even support it.

Dennis Doomen
  • 8,368
  • 1
  • 32
  • 44
  • 1
    FakeItEasy supports ordered assertions: https://github.com/FakeItEasy/FakeItEasy/wiki/Ordered-assertions. I agree it should be avoided where possible, but it can be useful in some circumstances. – David Tchepak Apr 22 '13 at 23:18
  • 2
    The book Pragmatic Unit Testing includes Time (order of things happening) because it is a perfectly valid specification to say, for example "Some validator needs to be called before calling save." or, in this case, "The encryptor needs to be called before uploading.". How else would you ensure this kind of specification in a method that calls void methods on it's dependencies? – Aaron Hawkins Mar 13 '15 at 16:58
  • Wouldn't it be a better test to make sure the uploaded result was encrypted? – Ryan Leach May 07 '18 at 09:12
  • I agree with @RyanTheLeach. You must only assert the observable outcome. I.e. the correct output or some exception. Don't test the internals. It'll lead to brittle unit tests. – Dennis Doomen May 08 '18 at 10:00