Poprawność testów jednostkowych

0

Cześć czy te dwa testy dobrze testuja mój PostService. Są poprawne pod kątem standardów ?

    [TestFixture]
    public class PostServiceShould
    {
        [Test]
        public void Add_New_Post_To_Context()
        {
            var mockSet = new Mock<DbSet<Post>>();
            var mockCtx = new Mock<ApplicationDbContext>();

            mockCtx.Setup(x => x.Posts).Returns(mockSet.Object);
            PostService post = new PostService(mockCtx.Object);

            post.AddPost(new Post());

            mockCtx.Verify(s => s.Posts.Add(It.IsAny<Post>()), Times.Once());
            mockCtx.Verify(c => c.SaveChanges(), Times.Once());
        }

        [Test]
        public void Can_Short_Content_Post()
        {
            var mockSet = new Mock<DbSet<Post>>();
            var mockCtx = new Mock<ApplicationDbContext>();

            mockCtx.Setup(x => x.Posts).Returns(mockSet.Object);
            PostService postService = new PostService(mockCtx.Object);

            Post post = new Post();
            post.Content = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";

            string result = postService.GetShortContent(post.Content);
            string expected = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa...";
            Assert.AreEqual(expected, result);
        }
    }
    public class PostService : IPostService
    {
        private ApplicationDbContext db;

        public PostService(ApplicationDbContext _db)
        {
            db = _db;
        }
        public void AddPost(Post post)
        {
            post.DateOfAddition = DateTime.Now;
            post.ShortContent = GetShortContent(post.Content);
            db.Posts.Add(post);
            db.SaveChanges();
        }

        public string GetShortContent(string content)
        {
            string result = "";
            if(content == null)
            {
                return result;
            }
            else
            {
                if (content.Length > 40)
                {
                    result = content.Substring(0, 40);
                    result = result + "...";
                    return result;
                }
                else if (content.Length <= 40)
                {
                    result = content;
                }
                return result;
            }
        }
1

Add_New_Post_To_Context()
Zalatuje integracyjnym.
w dodatku testujesz konkretną implementację.
Druga rzeczą, teoretycznie nie testujemy frameworków. to jakbyś testował czyjś kod. no to jest trochę dziwne.
jesli coś takiego chcesz teestować, to bardziej na zasadzie warstwy abstrakcji, jakiegoś interfejsu Repozytorium, UoW, albo Serwisu. Jedną z zasad Unit Testów, jest ich izolacja.
Jkaby testować, czy Dodawanie do listy faktycznie dodaje element do listy.

  1. Dodałbym w kodzie komentarze dzięjąc test na mini-segmenty. Ułatwisz zycie sobie i innym dzieląc wg arrange-act-assert albo gherkinem (Given-When-Then), wtedy też łatwiej będzie Ci się testowało pojedyncze rzeczy.

Nie kazdy test, który świeci na zielono oznacza, że to dobry test ;)

Wydaje mi się, że do Twoich testów, właśnie punktem końcowym jest IPostService, chcesz więcej? zaimplementuj abstrakcyjną fabrykę DBContextów.
Nie musisz testować EntityFrameworka :P
Swoją drogą, nigdy nie widziałem, żeby inicjalizować DbSety w testach.
Ale może jeszcze nie wszystko widziałem.

1

Przede wszystkim, to GetShortContent nie powinno być publiczną metodą w klasie PostService. Albo to skracanie uznajmy za etap zapisywania postu i testujemy je przy okazji testowania AddPost, albo GetShortContent trzeba wynieść do jakiegoś helpera i testować niezależnie.

1

Przy testowaniu chodzi o to, czy Twoja logika dobrze działa w różnych przypadkach.
Czyli:

  • testujesz TYLKO SWÓJ kod
  • mockujesz wszystko co nie jest Twoje - zakładasz, że działa w każdym przypadku poprawnie
  • testujesz dla skrajnych i zwykłych przypadków

Co do GetShortContent to tak jak mówi @somekind. Ja bym to umieścił w klasie Post.

I to np. powinieneś przetestować w taki sposób, że sprawdzasz:

  • co zwraca, gdy content jest pusty
  • co zwraca, gdy content ma 40 znaków
  • co zwraca, gdy content ma więcej niż 40 znaków
  • co zwraca, gdy content ma poniżej 40 znaków

To wszystko możesz załatwić testcase'ami. Czyli tak naprawdę piszesz jeden kod, ale masz 4 testy :) Poczytaj o TestCase. Chociaż tutaj akurat chyba powinno być kilka metod (tak jak napisałem niżej) i do każdej z nich można dać kilka przypadków, np: content ma 45 znaków, 1024 i 2048
Ty tak naprawdę sprawdziłeś tylko jeden przypadek - content ma więcej niż 40 znaków. Nie wiesz, co się stanie za pół roku jak post będzie miał mniej znaków ;)
Musisz patrzeć właśnie pod tym kątem. Testy jednostkowe nie tylko służą do testowania aktualnego stanu aplikacji, ale także tego z przyszłości. Bo może się okazać, że kiedyś ktoś zrobi coś, że przy contencie, który ma mniej niż 40 znaków, program się wywali. Oczywiście wszystko ewoluuje i czasem będzie trzeba zmienić testy, a czasem okaże się, że ktoś czegoś nie przemyślał w kodzie.

Co do nazewnictwa, to tutaj tak naprawdę nie ma żadnego żelaznego standardu. Zazwyczaj stosuje się coś takiego:
NazwaMetody_Warunki_Rezultat, np:

GetShortContent_ContentLessThan40_ReturnsContent
GetShortContent_ContentMoreThan40_ReturnsFirst40CharsAndElipsis

SomeMethod_SomethingWrongHappens_Throws
(można dodać jeszcze jaki wyjątek powinien zosstać rzucony)

SomeMethod_UserInputIsInvalid_NotThrow

SomeMethod_Passes

Też nie ma się co silić na wymyślanie nazw przypadków testowych. Czasem jest to naturalne, czasem po prostu nic innego nie przychodzi do głowy jak tylko: SomeMethod_Passes.
Chociaż w takim przypadku ja bym się zastanawiał, czy faktycznie ten jeden test wyczerpuje mi wszystkie możliwości metody.

1 użytkowników online, w tym zalogowanych: 0, gości: 1