Bot releases are visible (Hide)
Published by oskardudycz 5 months ago
ApiSpecification<TProgram>.With(new XUnitLoggerProvider(testOutputHelper, appendScope: false)
Full Changelog: https://github.com/oskardudycz/Ogooreck/compare/0.8.1...0.8.2
Full Changelog: https://github.com/oskardudycz/Ogooreck/compare/0.8.0...0.8.1
Published by oskardudycz about 1 year ago
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:
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: https://github.com/oskardudycz/EventSourcing.NetCore/pull/222.
See details in Pull Request: https://github.com/oskardudycz/Ogooreck/pull/17
Published by oskardudycz about 1 year ago
Added support for .NET 6 and bumped dependencies.
Full Changelog: https://github.com/oskardudycz/Ogooreck/compare/0.6.0...0.7.0
Published by oskardudycz almost 2 years ago
Full Changelog: https://github.com/oskardudycz/Ogooreck/compare/0.5.1...0.6.0
Published by oskardudycz about 2 years ago
Updated ObjectFactory implementation to use Expression with memoization to create the new object instead of Activator. It removes the reflection magic and enhances performance.
Published by oskardudycz about 2 years ago
Added business logic specifications to allow testing:
See the documentation: https://github.com/oskardudycz/Ogooreck/tree/main#business-logic-testing.
by @oskardudycz in https://github.com/oskardudycz/Ogooreck/pull/9, https://github.com/oskardudycz/Ogooreck/pull/10
Full Changelog: https://github.com/oskardudycz/Ogooreck/compare/0.3.0...0.5.0
Published by oskardudycz over 2 years ago
Added a few helper methods:
Published by oskardudycz over 2 years ago
This dependency was redundant and fault, as returned location header URI may be different and not start with the request URI.
Published by oskardudycz over 2 years ago
Broke down the CREATED
status with Location Header checks to give the possibility of providing a custom header easier.
Now instead of just using the CREATED
method:
public Task RegisterProduct() =>
API.Given(
URI("/api/products"),
BODY(new RegisterProductRequest("abc-123", "Ogooreck"))
)
.When(POST)
.Then(CREATED);
You need to use also RESPONSE_LOCATION_HEADER
.
public Task RegisterProduct() =>
API.Given(
URI("/api/products"),
BODY(new RegisterProductRequest("abc-123", "Ogooreck"))
)
.When(POST)
.Then(CREATED, RESPONSE_LOCATION_HEADER());
By default, it will check if the prefix matches the Request URI, but you can provide your prefix, e.g.:
.Then(CREATED, RESPONSE_LOCATION_HEADER("/api/with/your/custom/prefix/"));
You can also use the location header check separately from the CREATED
helper.
Added also RESPONSE_ETAG_HEADER
, you can use it as:
public Task RegisterProduct() =>
API.Given(
URI("/api/products"),
BODY(new RegisterProductRequest("abc-123", "Ogooreck"))
)
.When(POST)
.Then(CREATED, RESPONSE_ETAG_HEADER(1));
It supports both weak and strong ETag format, by default using the weak one.
Added also RESPONSE_HEADERS
helper and aligned response headers check with it.
See the details in PR: https://github.com/oskardudycz/Ogooreck/pull/5
Published by oskardudycz over 2 years ago
It's the first working version of the Ogooreck. Ogooreck is a Sneaky Test library. It helps to write readable and self-documenting tests. Current available for API testing.
Main assumptions:
Links