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