Ayende proposed a new AAA syntax in Rhino Mocks. I was wondering how it mixed with BDD.
Given this.
using System.Security; public interface EmailService { void Send(string message); } public interface AuthenticationService { bool Authenticate(string username, string password); } public class SecureVault { private readonly AuthenticationService _authenticationService; private readonly EmailService _emailService; private readonly string _secret; public SecureVault(AuthenticationService authenticationService, EmailService emailService, string secret) { _authenticationService = authenticationService; _emailService = emailService; _secret = secret; } public string GetSecret(string username, string password) { if(_authenticationService.Authenticate(username, password)) { _emailService.Send(username + " successfully authenticated, and grabbed secret."); return _secret; } else { throw new SecurityException("Access denied!"); } } }
Using a stub test double, the if/else branch can be tested. This is called state based testing.
using System.Security; using NUnit.Framework; using NUnit.Framework.SyntaxHelpers; using Rhino.Mocks; namespace Specs_for_SecureVault { [TestFixture] public class When_getting_secret_with_successful_authentication { private string _secret; private EmailService _emailService; [SetUp] public void Init() { var authenticationService = MockRepository.GenerateStub<AuthenticationService>(); _emailService = MockRepository.GenerateMock<EmailService>(); authenticationService.Stub(x => x.Authenticate("mortenlyhr", "pass1234")).Return(true); var secureVault = new SecureVault(authenticationService, emailService, "mysecretvalue"); _secret = secureVault.GetSecret("mortenlyhr", "pass1234"); } [Test] public void Should_return_secret() { Assert.That(_secret, Is.EqualTo("mysecretvalue")); } } [TestFixture] public class When_getting_secret_with_failed_authentication { [Test] [ExpectedException(typeof(SecurityException))] public void Should_throw_exception() { var authenticationService = MockRepository.GenerateStub<AuthenticationService>(); var emailService = MockRepository.GenerateMock<EmailService>(); authenticationService.Stub(x => x.Authenticate("mortenlyhr", "pass1234")).Return(false); var secureVault = new SecureVault(authenticationService, emailService, "mysecretvalue"); secureVault.GetSecret("mortenlyhr", "pass1234"); } } }
So far so good, but what about interaction based testing?
The EmailService is a prime candidate for this. We can write an additional test for this expectation, in the "When_getting_secret_with_successful_authentication".
[Test] public void Should_send_an_email_containing_the_username() { _emailService.AssertWasCalled(x=>x.Send(Arg<string>.Matches(s=>s.Contains("mortenlyhr")))); }
I don't think it can be any clearer that this, thank you BDD and Rhino Mocks ;-) - A match from heaven...
I am starting to see a pattern where mocks are private fields, and stubs have their return values as private fields. This is a lot less noise than when I did TDD, where every dependency was a private field. So again BDD helps you write less brittle specifications.
No comments:
Post a Comment