Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Async UnitTests setup in c#

I need to have a logged in user for each of my unit tests, which forces me to do an async call (the login) in my test SetUp.

I can't find a way to make this work, I either get null pointer exceptions, or invalid signatures for setup.

public async void SetUp() {}

This makes all my test fail with my objects probably because I'm not logged in.

public async Task SetUp() {}

Makes all my test ignored because the setup has an invalid signature.

And I would like not to have to copy my X lines of setup in each test, since they're all exactly the same and... that's what the setup is for.

What am I missing ? this appears like a trivial problem.

Here is what I have now, for the sake of showing something

CreateTicketViewModel _viewModel;

        [SetUp()]
        public async void SetUp()    //I have tried using Task instead of void
        {

            IUserService userService = Dependency.Instance.Resolve<IUserService>();
            await userService.LoginAsync(this.UserName, this.Password);

            _viewModel = Dependency.Instance.Resolve<CreateTicketViewModel>();
        }

        [TearDown()]
        public void TearDown()
        {
            _viewModel = null;  // I have tried removing this
        }

        [Test()]
        public void Initialization()
        {
            // If I put what's in SetUp here and add "async" before void,
            // it works just fine

            Assert.IsNotNull(_viewModel);
            Assert.IsNotNull(_viewModel.Ticket);
        }
like image 364
Gil Sand Avatar asked Oct 20 '15 14:10

Gil Sand


3 Answers

Depending on the unit test framework you use async setup may not be handled correctly by the framework.

in NUnits case I think it doesn't support yet async Setup methods.

So what you should do in your Setup is to just wait synchronously for the Login to complete:

userService.LoginAsync(this.UserName, this.Password).Wait();

EDIT: looks like it's an open issue https://github.com/nunit/nunit/issues/60

It's the case for MSTests also.

like image 62
Dan Dinu Avatar answered Oct 17 '22 10:10

Dan Dinu


Could you not just make your SetUp method not async at all and write this?

new Task(() => userService.LoginAsync(this.UserName, this.Password)).RunSynchronously()
like image 20
Evren Kuzucuoglu Avatar answered Oct 17 '22 11:10

Evren Kuzucuoglu


Async Setups are not supported but async test methods are supported. You can make your test methods async instead of setup method.

[TestFixture]
public class AsyncSetupTest
{
    private Task<CreateTicketViewModel> viewModelTask;

    [SetUp()]
    public void SetUp()
    {
        viewModelTask = Task.Run(async () =>
        {
            IUserService userService = Dependency.Instance.Resolve<IUserService>();
            await userService.LoginAsync(this.UserName, this.Password);

            return Dependency.Instance.Resolve<CreateTicketViewModel>();
        });
    }

    [Test()]
    public async Task Initialization()
    {
        CreateTicketViewModel viewModel = await viewModelTask;

        Assert.IsNotNull(viewModel);
        Assert.IsNotNull(viewModel.Ticket);
    }
}

Idea is, instead of getting all the setup work done in Setup method, we create a Task representing the completion of setup and awaiting it in the Test method.

This way you're not repeating all the setup logic. But just extracting the ViewModel from Task inside all test methods.

like image 22
Sriram Sakthivel Avatar answered Oct 17 '22 11:10

Sriram Sakthivel