Skip to content
Wojtek edited this page Jul 4, 2023 · 2 revisions

Page version: 3.x

This page contains a set of utilities that help with writing tests.

State<T>

LightBDD.Framework.State<T> is a structure that protects scenario state fields from access without former initialization.

When scenario has field which is initialized during execution of scenario steps, it is possible to wrap such field type with this structure. In case the field value is obtained without former initialization, an InvalidOperationException will be thrown with message indicating the field type or name that has been accessed. Typically, in the same case when State<T> is not used, an NullReferenceException would be thrown, providing much less details about actual problem.

Example usage:

public class My_feature : FeatureFixture
{
    // field wrapped with State<T>
    private State<Customer> _customer;
    // property providing access to the field, which will throw InvalidOperationException if field was not initialized
    private CustomerRecord Customer => _customer.GetValue(nameof(Customer));

    private void Given_customer_with_email(string email)
    {
        // Customer state initalization
        _customer = new CustomerRecord { Email = email };
    }

    private void When_update_customer_with_phone(string phone)
    {
        // Customer field access, which will throw InvalidOperationException if field was not initialized
        Customer.Phone = phone;
        SaveCustomer(_customer);
    }

    private void Then_customer_phone_should_have_value(string value)
    {
        // Customer field access, which will throw InvalidOperationException if field was not initialized
        Assert.Equal(value, Customer.Phone);
    }
}

Note: The State<T> is mentioned on Scenario State Management | Ensuring state is initialized before use wiki page

MessageListener

LightBDD.Framework.Messaging.MessageListener class allows listening for messages and recording them for further assertions. The class is useful in the scenarios where asynchronous operations are involved, such as callbacks or events.

Lets imagine following scenario:

Given_I_have_items_in_basket(),
When_I_call_OrderService_Api_to_place_and_order(),
Then_I_the_OrderService_Api_response_should_contain_order_id(),
Then_an_OrderPlacedEvent_should_be_published_for_this_order_id()

The OrderPlacedEvent is an asynchronous service bus event that Order Service fires upon order placement completion. The Order Service itself may fire this event either before returning Api call or after, if the operation is processed in the background, which depends on the service implementation.

The MessageListener class allows implementing the final step as:

private async Task Then_an_OrderPlacedEvent_should_be_published_for_this_order_id()
{
     await _listener.EnsureReceived<OrderPlacedEvent>(x => x.OrderId == _order.Id);
}

where the listener will return immediately if the matching message was already received or wait for the pre-defined time until it arrives.

In order to use the MessageListener, it has to be started before the Api call is made (for example in the feature fixture constructor) and disposed after it is no longer used. Please note that disposal of the listener is necessary, otherwise it will continue accumulating messages after test is completed, increasing memory consumption of the test suite.

The MessageListener can be started by calling MessageListener.Start(source) where the source is a test specific implementation of:

public interface IMessageSource
{
    public event Action<object> OnMessage;
}

LightBDD does not provide any implementation of IMessageSource, but assuming the tests uses queueing mechanisms like Rebus, NServiceBus, or similar, the implementation of this interface would be a handler of messages coming from the service bus.

The WebApi Service Tests with mocked dependencies tutorial presents usage of MessageListener.

Continue reading: Test framework integrations

Clone this wiki locally