Στην προσπάθεια που κάνω να βελτιώσω τον τρόπο που γράφω τεστ για τον κώδικα δεν μπορώ να αποφασίσω με σιγουριά πως ακριβώς πρέπει να γράφεται ένα τεστ. Σύμφωνα με αυτό που θεωρείται best practice το τεστ πρέπει να αφορά και να τεστάρει ένα και μόνο πράγμα. Έτσι δεν ξέρω αν το να τεστάρω τόσο την κατάσταση-αποτέλεσμα (state) όσο και την συμπεριφορά (behavior) του συστήματος στο ίδιο τεστ δεν είναι καλή πρακτική.
Για να γίνω πιο κατανοητός θα παραθέσω ένα παράδειγμα με κώδικα.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
| public class UsersService
{
private readonly IUnitOfWorkFactory _unitOfWorkFactory;
private readonly IUsersRespository _repository;
public UsersService(IUnitOfWorkFactory unitOfWorkFactory, IUsersRespository repository)
{
_unitOfWorkFactory = unitOfWorkFactory;
_repository = repository;
}
public long Insert(User user)
{
using (var uow = _unitOfWorkFactory.Create())
{
_repository.InsertOrUpdate(user);
uow.Commit();
return user.Id;
}
}
}
public interface IUsersRespository
{
void InsertOrUpdate(User user);
}
public interface IUnitOfWorkFactory
{
IUnitOfWork Create();
}
public interface IUnitOfWork : IDisposable
{
void Commit();
} |
Και θέλουμε να τεστάρουμε την μέθοδο insert του service. Αυτό που μπορώ να σκεφτώ είναι δυο προσεγγίσεις. Η πρώτη σε μια μέθοδο τεστάρουμε τόσο την σωστή αλληλεπίδραση μεταξύ των objects αλλά και το αποτέλεσμα που επιστρέφει η μέθοδος. Ο κώδικας μοιάζει με τον παρακάτω:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| public void TestInsert_ValidUser_ReturnsId()
{
//Arrange
_mockedRepository.Setup(r => r.InsertOrUpdate(It.IsAny<User>())).Callback((User u) => { u.Id++; });
User newUser = new User {UserName = "username", Password = "Password"};
//Act
var result = _service.Insert(newUser);
//Assert
Assert.AreNotEqual(0, result, "Id not set");
_mockedUnitOfWorkFactory.Verify(uof => uof.Create(), Times.AtLeast(1));
_mockedRepository.Verify(r => r.InsertOrUpdate(It.IsAny<User>()), Times.Once());
_mockedUnitOfWork.Verify(uow=>uow.Commit(), Times.Once());
} |
Έτσι σε αυτή την μέθοδο τεστάρουμε τόσο ότι η οντότητα απέκτησε κάποιο Id διαφορετικό από το 0 (state) όσο και το ότι κλήθηκαν μέθοδοι στα άλλα αντικείμενα (behavior).
Η άλλη προσέγγιση που μπορώ να σκεφτώ είναι χωρίζουμε το παραπάνω τεστ σε τέσσερις διαφορετικές μεθόδους που η κάθε μία από αυτή θα είναι υπεύθυνη για έναν και μόνο έλεγχο.
Για παράδειγμα η μια θα ελέγχει για το Id η άλλη ότι κλήθηκε η μέθοδος create τουλάχιστον μια φορά κ.ο.κ.
Προσωπικά προτιμώ την πρώτη προσέγγιση μιας δεν υπάρχουν τόσες πολλές μέθοδοι για συντήρηση. Από την άλλη βέβαια η συγκεκριμένη μέθοδος εξαρτάται πάρα πολύ από την συγκεκριμένη υλοποίηση και σε μια αλλαγή θα πρέπει να αλλάξει αντίστοιχα και το τεστ.
Περιμένω να ακούσω γνώμες και οποιεσδήποτε παρατηρήσεις πάνω στο θέμα.
My dream is to fly over the rainbow so high!!!!