Skip to content

0.8.0

Compare
Choose a tag to compare
@oskardudycz oskardudycz released this 15 Aug 13:28
· 4 commits to main since this release

Refactored API Specification syntax to enable easier test setup.

To do that, it was necessary to perform breaking changes and move the request builder from Given to When. Still, accidentally it also improved the test clarity, as too often it just had empty Given.

Consolidation of the request building also enabled more improvements like:

  • easier test setup by just passing the request definition without the need for the dedicated fixtures,
  • using values from the response results to shape the url or in assertions (e.g. using created id from location header),
  • adding a description to given and then.

I also introduced test context that may eventually be used for building advanced reporters - e.g. markdown documentation.

Migration:

Move request builders from Given to Then. Previous:

public Task GET_ReturnsShoppingCartDetails() =>
    API.Given(
            URI($"/api/ShoppingCarts/{API.ShoppingCartId}")
        )
        .When(GET_UNTIL(RESPONSE_SUCCEEDED))
        .Then(
            OK,
            RESPONSE_BODY(new ShoppingCartDetails
            {
                Id = API.ShoppingCartId,
                Status = ShoppingCartStatus.Confirmed,
                ProductItems = new List<PricedProductItem>(),
                ClientId = API.ClientId,
                Version = 2,
            }));

Now:

public Task GET_ReturnsShoppingCartDetails() =>
    API.Given()
        .When(GET, URI($"/api/ShoppingCarts/{API.ShoppingCartId}"))
        .Until(RESPONSE_SUCCEEDED)
        .Then(
            OK,
            RESPONSE_BODY(new ShoppingCartDetails
            {
                Id = API.ShoppingCartId,
                Status = ShoppingCartStatus.Confirmed,
                ProductItems = new List<PricedProductItem>(),
                ClientId = API.ClientId,
                Version = 2,
            }));

Also, you can change your advanced setup from (using XUnit):

using Carts.Api.Requests;
using Carts.ShoppingCarts;
using Carts.ShoppingCarts.GettingCartById;
using Carts.ShoppingCarts.Products;
using FluentAssertions;
using Ogooreck.API;
using static Ogooreck.API.ApiSpecification;
using Xunit;

namespace Carts.Api.Tests.ShoppingCarts.AddingProduct;

public class AddProductFixture: ApiSpecification<Program>, IAsyncLifetime
{
    public Guid ShoppingCartId { get; private set; }

    public readonly Guid ClientId = Guid.NewGuid();

    public async Task InitializeAsync()
    {
        var openResponse = await Send(
            new ApiRequest(POST, URI("/api/ShoppingCarts"), BODY(new OpenShoppingCartRequest(ClientId)))
        );

        await CREATED(openResponse);
        await RESPONSE_LOCATION_HEADER()(openResponse);

        ShoppingCartId = openResponse.GetCreatedId<Guid>();
    }

    public Task DisposeAsync() => Task.CompletedTask;
}

public class AddProductTests: IClassFixture<AddProductFixture>
{
    private readonly AddProductFixture API;

    public AddProductTests(AddProductFixture api) => API = api;

    [Fact]
    [Trait("Category", "Acceptance")]
    public async Task Post_Should_AddProductItem_To_ShoppingCart()
    {
        var product = new ProductItemRequest(Guid.NewGuid(), 1);

        await API
            .Given(
                URI($"/api/ShoppingCarts/{API.ShoppingCartId}/products"),
                BODY(new AddProductRequest(product)),
                HEADERS(IF_MATCH(1))
            )
            .When(POST)
            .Then(OK);

        await API
            .Given(URI($"/api/ShoppingCarts/{API.ShoppingCartId}"))
            .When(GET_UNTIL(RESPONSE_ETAG_IS(2)))
            .Then(
                RESPONSE_BODY<ShoppingCartDetails>(details =>
                {
                    details.Id.Should().Be(API.ShoppingCartId);
                    details.Status.Should().Be(ShoppingCartStatus.Pending);
                    details.ProductItems.Should().HaveCount(1);
                    details.ProductItems.Single().ProductItem.Should()
                        .Be(ProductItem.From(product.ProductId, product.Quantity));
                    details.Version.Should().Be(2);
                })
            );
    }
}

to

using Carts.Api.Requests;
using Carts.ShoppingCarts;
using Carts.ShoppingCarts.GettingCartById;
using FluentAssertions;
using Ogooreck.API;
using Xunit;
using static Ogooreck.API.ApiSpecification;

namespace Carts.Api.Tests.ShoppingCarts.AddingProduct;

public class AddProductTests: IClassFixture<ApiSpecification<Program>>
{
    private readonly ApiSpecification<Program> API;

    public AddProductTests(ApiSpecification<Program> api) => API = api;

    private readonly ProductItemRequest product = new(Guid.NewGuid(), 1);

    [Fact]
    [Trait("Category", "Acceptance")]
    public Task Post_Should_AddProductItem_To_ShoppingCart() =>
        API.Given("Opened Shopping Cart", OpenShoppingCart())
            .When(
                "Add new product",
                POST,
                URI(ctx => $"/api/ShoppingCarts/{ctx.GetCreatedId()}/products"),
                BODY(new AddProductRequest(product)),
                HEADERS(IF_MATCH(1))
            )
            .Then(OK)
            .And()
            .When(
                "Get updated shopping cart details",
                GET,
                URI(ctx => $"/api/ShoppingCarts/{ctx.GetCreatedId()}")
            )
            .Then(
                RESPONSE_BODY<ShoppingCartDetails>((details, ctx) =>
                {
                    details.Id.Should().Be(ctx.GetCreatedId());
                    details.Status.Should().Be(ShoppingCartStatus.Pending);
                    var productItem = details.ProductItems.Single();
                    productItem.Quantity.Should().Be(product.Quantity);
                    productItem.ProductId.Should().Be(product.ProductId!.Value);
                    details.Version.Should().Be(2);
                })
            );

    public static RequestDefinition OpenShoppingCart(Guid? clientId = null) =>
        SEND(
            "Open ShoppingCart",
            POST,
            URI("/api/ShoppingCarts"),
            BODY(new OpenShoppingCartRequest(clientId ?? Guid.NewGuid()))
        );

}

See also example migration from the old syntax: oskardudycz/EventSourcing.NetCore#222.

See details in Pull Request: #17