DateTime w testach integracyjnych

0

Mam taki test:

        [Fact]
        public async Task Should_Create_Group()
        {
            UseAuthenticatedRequest();

            var command = new CreateGroup
            {
                Name = "G4",
                Description = "Test Description"
            };
            
            var response = await Client.PostAsync("/api/groups", CreatePayload(command));
            response.EnsureSuccessStatusCode();
            response.Headers.Location.ShouldNotBeNull();

            var group = await GetGroup(GetResourceIdFromPath(response.Headers.Location.LocalPath));
            group.Name.ShouldBe(command.Name);
            group.Description.ShouldBe(command.Description);
            group.CreatedAt.ShouldBe(/* ? */);
        }

Przed zapisaniem do bazy robię coś w stylu group.CreatedAt = DateTime.UtcNow. Jak mam napisać test, żeby mieć pewność, że nie zapomniałem przypadkiem ustawić daty utworzenia? Próbowałem zautomatyzować takie operacje, nadpisując SaveChanges, ale było z tym więcej problemów niż korzyści. ;(

2

Może coś w stylu tą pomiędzy wywołaniem requestu a odbiorem danych. Czyli prze PostAsync ustawiasz w teście datę. Potem po wywołaniu clienta. A group.CreatedAt powinna być pomiędzy. Ja pewnie szedłbym w te stronę. A to jest twoje Api czy może mockujesz tego clienta?. Bo ja jak miałem podobne rzeczy to raczej robiłem mocka i sprawdzałęm jak się zachowuje kod w zależności od responsa

1

Myślałem też, żeby napisać sobie abstrakcję nad DateTime (IDateTimeProvider) i wstrzykiwać ją do serwisów. Wtedy mógłbym sobie w testach integracyjnych podstawić implementację zwracającą zawsze jakąś określoną datę. Ale to chyba za dużo kombinowania.

7
nobody01 napisał(a):

Myślałem też, żeby napisać sobie abstrakcję nad DateTime (IDateTimeProvider) i wstrzykiwać ją do serwisów. Wtedy mógłbym sobie w testach integracyjnych podstawić implementację zwracającą zawsze jakąś określoną datę. Ale to chyba za dużo kombinowania.

A ja takie podejście z abstrakcją ponad datą polecam. Dzięki temu możesz także w łatwy sposób testować niestandardowe sytuacje jak 29 tutego oraz zmiana roku.

1
nobody01 napisał(a):

Przed zapisaniem do bazy robię coś w stylu group.CreatedAt = DateTime.UtcNow. Jak mam napisać test, żeby mieć pewność, że nie zapomniałem przypadkiem ustawić daty utworzenia?

No jeśli potem to odczytujesz z bazy, to przecież znaczy, że ustawiłeś.

Próbowałem zautomatyzować takie operacje, nadpisując SaveChanges, ale było z tym więcej problemów niż korzyści. ;(

Jakich problemów? O ile dobrze pamiętam to raptem jakieś kilkanaście linijek kodu.

Taki wrapper na datetime z interfejsem ma może sens, ale pod kątem testów jednostkowych, nie integracyjnych.

0
somekind napisał(a):

No jeśli potem to odczytujesz z bazy, to przecież znaczy, że ustawiłeś.

Mógłbyś rozwinąć? Jeśli mam asercję group.CreatedAt.ShouldBe(...), to co mam tu wpisać?

Jakich problemów? O ile dobrze pamiętam to raptem jakieś kilkanaście linijek kodu.

Tak jak myślę o tym, próbując przewidzieć Twoje kontrargumenty, to tak naprawdę te problemy nie są jakieś znaczące. ;) Pierwszym miało być to, że ograniczam się do posiadania zawsze właściwości o tej samej nazwie, np. CreatedById, podczas gdy czasami lepiej jest dać np. OwnerId lub AuthorId. Ale jeśli mam anemiczny model i CRUDa, to IMO da się z tym żyć. Co prawda trzeba będzie trochę pokonfigurować AutoMappera, ale poprawność tych konfiguracji można łatwo sprawdzić w jednym teście. Drugim problemem miała być mniejsza czytelność w sytuacji, gdy część właściwości ustawiamy w serwisie, a część w SaveChanges, ale to deż dyskusyjne.

Taki wrapper na datetime z interfejsem ma może sens, ale pod kątem testów jednostkowych, nie integracyjnych.

Czyli co sugerujesz? Wstrzykiwać IDateTimeProvider tylko wtedy, gdy faktycznie trzeba coś przetestować jednostkowo, a używać DateTime.UtcNow w pozostałych przypadkach?

2
nobody01 napisał(a):
somekind napisał(a):

No jeśli potem to odczytujesz z bazy, to przecież znaczy, że ustawiłeś.

Może .ShouldNot(Be.Empty);?
A jeśli kolumna w bazie jest NOT NULL, to nawet i tego nie trzeba, bo przecież nie zapiszesz jeśli nie ustawisz.

Tak jak myślę o tym, próbując przewidzieć Twoje kontrargumenty, to tak naprawdę te problemy nie są jakieś znaczące. ;) Pierwszym miało być to, że ograniczam się do posiadania zawsze właściwości o tej samej nazwie, np. CreatedById, podczas gdy czasami lepiej jest dać np. OwnerId lub AuthorId.

Ale czemu? OwnerId czy AuthorId sugerują jakieś biznesowe znaczenie, a audyt nie jest przecież częścią biznesu. A więc nawet jeśli już masz taką właściwość biznesową, to możesz też mieć oddzielne CreatedBy i zgodność z konwencją.

Drugim problemem miała być mniejsza czytelność w sytuacji, gdy część właściwości ustawiamy w serwisie, a część w SaveChanges, ale to deż dyskusyjne.

Poza SaveChanges tych właściwości przecież używać nie będziemy, więc nie trzeba się o nie martwić.

Czyli co sugerujesz? Wstrzykiwać IDateTimeProvider tylko wtedy, gdy faktycznie trzeba coś przetestować jednostkowo, a używać DateTime.UtcNow w pozostałych przypadkach?

Ja sugeruję nie mockować w testach integracyjnych. Przecież taki test jest wówczas zupełnie bezwartościowy, bo nie testuje się produkcyjnego kodu tylko jakieś mocki.

2

Jedna z rzeczy ktora u mnie w firmie caly czas podkreslaja zeby nie projektowac API pod testy. Moim zdaniem pierwsza odpowiedz ma na najwiecej sensu (sprawdzic czas przed i po requescie i powinna byc pomiedzy.

Moim zdaniem nie warto gmatwac kodu tylko po to zeby raz na 4 lata nie miec buga bo zagmatwany kod ma srednio wiecej bugow wiec srednio dodasz sobie i wszystkim co tego kodu korzystaja wiecej roboty.

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