Dopo aver letto l'avvio rapido su nspec.org, mi sono reso conto che NSpec potrebbe essere uno strumento utile in uno scenario che stava diventando un po 'ingombrante con NUnit da solo.
Sto aggiungendo un OAuth (o, DotNetOpenAuth) a un sito web e rapidamente ho fatto un casino di scrivere metodi di test come
[Test]
public void UserIsLoggedInLocallyPriorToInvokingExternalLoginAndExternalLoginSucceedsAndExternalProviderIdIsNotAlreadyAssociatedWithUserAccount()
{
...
}
... e mi sono ritrovato con una dozzina di permutazioni di questo tema, dato che l'utente si stava già loggando localmente e non localmente, l'accesso esterno aveva esito positivo o negativo, ecc. Non solo i nomi dei metodi erano ingombranti, ma ogni il test richiedeva una configurazione che contenesse parti in comune con una serie diversa di altri test.
Mi sono reso conto che le funzionalità di configurazione incrementale di NSpec avrebbero funzionato benissimo per questo, e per un certo periodo stavo trasportando un lungo meraviglioso, con codice come
act = () => { actionResult = controller.ExternalLoginCallback(returnUrl); };
context["The user is already logged in"] = () =>
{
before = () => identity.Setup(x => x.IsAuthenticated).Returns(true);
context["The external login succeeds"] = () =>
{
before = () => oauth.Setup(x => x.VerifyAuthentication(It.IsAny<string>())).Returns(new AuthenticationResult(true, providerName, "provideruserid", "username", new Dictionary<string, string>()));
context["External login already exists for current user"] = () =>
{
before = () => authService.Setup(x => x.ExternalLoginExistsForUser(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>())).Returns(true);
it["Should add 'login sucessful' alert"] = () =>
{
var alerts = (IList<Alert>)controller.TempData[TempDataKeys.AlertCollection];
alerts[0].Message.should_be_same("Login successful");
alerts[0].AlertType.should_be(AlertType.Success);
};
it["Should return a redirect result"] = () => actionResult.should_cast_to<RedirectToRouteResult>();
};
context["External login already exists for another user"] = () =>
{
before = () => authService.Setup(x => x.ExternalLoginExistsForAnyOtherUser(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>())).Returns(true);
it["Adds an error alert"] = () =>
{
var alerts = (IList<Alert>)controller.TempData[TempDataKeys.AlertCollection];
alerts[0].Message.should_be_same("The external login you requested is already associated with a different user account");
alerts[0].AlertType.should_be(AlertType.Error);
};
it["Should return a redirect result"] = () => actionResult.should_cast_to<RedirectToRouteResult>();
};
Questo approccio ha funzionato magnificamente fino a quando non mi sono preparato a scrivere il codice di test per il mio livello ApplicationServices, al quale delegare la manipolazione viewmodel dai miei controller MVC e che coordina le operazioni del livello di repository dati inferiore:
public void CreateUserAccountFromExternalLogin(RegisterExternalLoginModel model)
{
throw new NotImplementedException();
}
public void AssociateExternalLoginWithUser(string userName, string provider, string providerUserId)
{
throw new NotImplementedException();
}
public string GetLocalUserName(string provider, string providerUserId)
{
throw new NotImplementedException();
}
Non ho idea di quale sia il mondo per nominare la classe di test, i metodi di test, o anche se dovrei includere il test per questo livello nella classe di test dal mio frammento di codice grande sopra, in modo che una singola funzione o l'azione dell'utente potrebbe essere testata senza riguardo per la stratificazione architettonica.
Non riesco a trovare tutorial o post di blog che coprano più di semplici esempi, quindi gradirei eventuali consigli o indicassi la giusta direzione. Mi piacerebbe anche che la risposta "la tua domanda non sia valida" risponda a una domanda purché sia fornita una spiegazione.