Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nullable Reference Types (C# 8.0) and MSpec Tests #399

Open
alexringeri-xero opened this issue Feb 5, 2020 · 2 comments
Open

Nullable Reference Types (C# 8.0) and MSpec Tests #399

alexringeri-xero opened this issue Feb 5, 2020 · 2 comments

Comments

@alexringeri-xero
Copy link

I am encountering compilation errors when enabling the 'nullable reference types' language feature on an MSpec example. Is there a recommended approach for writing idiomatic MSpec tests with nullable reference types enabled?
https://docs.microsoft.com/en-us/dotnet/csharp/nullable-references
https://devblogs.microsoft.com/dotnet/embracing-nullable-reference-types/

Example:

[Subject("Authentication")]
class When_authenticating_an_admin_user
{
    static SecurityService subject;
    static UserToken user_token;

    Establish context = () =>
        subject = new SecurityService(foo, bar);

    Because of = () =>
        user_token = subject.Authenticate("username", "password");

    It should_indicate_the_users_role = () =>
        user_token.Role.ShouldEqual(Roles.Admin);

    It should_have_a_unique_session_id = () =>
        user_token.SessionId.ShouldNotBeNull();
}

Error:

Non-nullable field 'subject' is uninitialized. Consider declaring the field as nullable.
One approach to resolve the issue is to declare subject and user_token as nullable, and assume they are not null after the Establish step. We can use the null-forgiving operator (!) to assume the reference is not null. This makes the tests harder to read and harder to determine the test intent.

I am looking for a better approach.

Updated Example:

[Subject("Authentication")]
class When_authenticating_an_admin_user
{
    static SecurityService? subject;
    static UserToken? user_token;

    Establish context = () =>
        subject = new SecurityService(foo, bar);

    Because of = () =>
        user_token = subject!.Authenticate("username", "password");

    It should_indicate_the_users_role = () =>
        user_token!.Role.ShouldEqual(Roles.Admin);

    It should_have_a_unique_session_id = () =>
        user_token!.SessionId.ShouldNotBeNull();
}
@robertcoltheart
Copy link
Member

Bottom line, no we don't have a tried and tested way of doing this yet. I'm in the middle of an extensive refactoring that will address things like async/await support, so I'll add this to the list of things we need to address.

@RedwoodForest
Copy link

@alexringeri-xero

An alternative to your updated example is to move the null-forgiving operator to the initialization of the fields. I think this has less of an impact on test readability:

[Subject("Authentication")]
class When_authenticating_an_admin_user
{
    static SecurityService subject = null!;
    static UserToken? user_token = null!;

    Establish context = () =>
        subject = new SecurityService(foo, bar);

    Because of = () =>
        user_token = subject.Authenticate("username", "password");

    It should_indicate_the_users_role = () =>
        user_token.Role.ShouldEqual(Roles.Admin);

    It should_have_a_unique_session_id = () =>
        user_token.SessionId.ShouldNotBeNull();
}

This will prevent the nullable reference types support from detecting bugs like using these fields before you initialize them in setup though (essentially the same as in your updated example).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants