Je code depuis environ 12 ans, mais je ne me suis jamais habitué à TDD dans tout ce temps. Eh bien, les choses sont sur le point de changer, mais puisque j'apprends tout seul, j'espère que vous pourriez m'aider.Meilleures pratiques pour cette tentative TDD
Je poste un exemple de jeu pour une classe de poitrine TRÈS SIMPLE. Lorsque le joueur attrape un coffre, il enregistre l'heure actuelle à laquelle il a été obtenu. Cette poitrine prend un certain temps à s'ouvrir, donc, j'ai besoin, pour des raisons d'interface utilisateur, de montrer le temps qu'il faut pour ouvrir. Chaque coffre a un type, et ce type est lié à une valeur de base de données de combien de temps il faudrait pour ouvrir.
Il s'agit d'un "test-pas-juste-choses-faites-rapide-esprit". Considérez que ChestsDatabase et DateManager sont des singletons contenant les valeurs liées à la base de données et l'heure système actuelle enveloppée dans une classe.
public class Chest {
private readonly int _type;
private readonly float _timeObtained;
public Chest(int type, float timeObtained) {
_type = type;
_timeObtained = timeObtained;
}
public bool IsOpened() {
return GetRemainingTime() <= 0;
}
// It depends heavily on this concrete Singleton class
public float GetRemainingTime() {
return ChestsDatabase.Instance.GetTimeToOpen(_type) - GetPassedTime();
}
// It depends heavily on this concrete Singleton class
private float GetPassedTime() {
return DateManager.Instance.GetCurrentTime() - _timeObtained;
}
}
Bien sûr, je aurais pu faire dans une dépendance de la mode d'injection et de se débarrasser des singletons:
public class Chest {
private readonly ChestsDatabase _chestsDatabase;
private readonly DateManager _dateManager;
private readonly int _type;
private readonly float _timeObtained;
public Chest(ChestsDatabase chestsDatabase, DateManager dateManager, int type, float timeObtained) {
_chestsDatabase = chestsDatabase;
_dateManager = dateManager;
_type = type;
_timeObtained = timeObtained;
}
public bool IsOpened() {
return GetRemainingTime() <= 0;
}
public float GetRemainingTime() {
return _chestsDatabase.GetTimeToOpen(_type) - GetPassedTime();
}
private float GetPassedTime() {
return _dateManager.GetCurrentTime() - _timeObtained;
}
}
Et si j'utilise des interfaces pour exprimer la même logique? Cela va être beaucoup plus "TDD-friendly", non? (En supposant que je l'ai fait d'abord les tests, bien sûr)
public class Chest {
private readonly IChestsDatabase _chestsDatabase;
private readonly IDateManager _dateManager;
private readonly int _type;
private readonly float _timeObtained;
public Chest(IChestsDatabase chestsDatabase, IDateManager dateManager, int type, float timeObtained) {
_chestsDatabase = chestsDatabase;
_dateManager = dateManager;
_type = type;
_timeObtained = timeObtained;
}
public bool IsOpened() {
return GetRemainingTime() <= 0;
}
public float GetRemainingTime() {
return _chestsDatabase.GetTimeToOpen(_type) - GetPassedTime();
}
private float GetPassedTime() {
return _dateManager.GetCurrentTime() - _timeObtained;
}
}
Mais comment diable suis-je censé tester quelque chose comme ça? Serait-ce comme ça?
[Test]
public void SomeTimeHavePassedAndReturnsRightValue()
{
var mockDatabase = new MockChestDatabase();
mockDatabase.ForType(0, 5); // if Type is 0, then takes 5 seconds to open
var mockManager = new MockDateManager();
var chest = new Chest(mockDatabase, mockManager, 0, 6); // Got a type 0 chest at second 6
mockManager.SetCurrentTime(8); // Now it is second 8
Assert.AreEqual(3, chest.GetRemainingTime()); // Got the chest at second 6, now it is second 8, so it passed 2 seconds. We need 5 seconds to open this chest, so the remainingTime is 3
}
Est-ce logiquement correct? Est-ce que je manque quelque chose? Parce que cela semble si grand, si compliqué, si ... faux. J'ai dû créer 2 classes supplémentaires ~ juste ~ pour ces tests.
Voyons voir un cadre moqueur:
[Test]
public void SomeTimeHavePassedAndReturnsRightValue()
{
var mockDatabase = Substitute.For<IChestsDatabase>();
mockDatabase.GetTimeToOpen(0).Returns(5);
var mockManager = Substitute.For<IDateManager>();
var chest = new Chest(mockDatabase, mockManager, 0, 6);
mockManager.GetCurrentTime().Returns(8);
Assert.AreEqual(3, chest.GetRemainingTime());
}
je me suis débarrassé de deux classes avec le cadre, mais encore, je sens qu'il ya quelque chose de mal. Y a-t-il un moyen plus simple dans ma logique? Dans ce cas, utiliseriez-vous un cadre moqueur ou des classes implémentées? Est-ce que vous vous débarrasseriez complètement des tests ou insisteriez-vous sur l'une de mes solutions? Ou comment rendre cette solution meilleure?
J'espère que vous pouvez m'aider dans mon voyage TDD. Merci.