specklesystems / xUnitRevit

xUnit runner for Revit
https://speckle.systems/blog/xunitrevit/
MIT License
120 stars 44 forks source link

Suggestion - Get Revit context when testing #2

Closed Dre-Tas closed 4 years ago

Dre-Tas commented 4 years ago

Hi guys! It's-a me again 😄

Not a bug, just a suggestion/request that I'd like to run through you to see if it makes sense.

Would it make sense to expose the context where the test runner is operating (i.e. the Application in Revit terms)?

This is my case: I created an object called RevitModel, that in some aspects wraps up Revit's Document object, but has a few other properties/methods. One of the methods creates a new Revit file with Application.NewProjectDocument(string templatePath), which is a method of the Application object. So I can't really test my method that creates a RevitModel because I can't get the Application that is running the test runner.

So what I'm thinking is, can we expose publicly xru.Uiapp so that it can be used to create objects? Or has it been set to private because that's the way it's has to be for some reason (like I am doing something that I'm not supposed to do in unit testing)?

teocomi commented 4 years ago

Hey Tas, yep that makes sense. We've currently limited the functionalities to what our use cases require, but we're happy to update to suit others needs!

Feel free to submit a PR with the change.

Dre-Tas commented 4 years ago

Yeah ok glad I was making sense! And no worries, Expect a PR from me soon 👍

Dre-Tas commented 4 years ago

I've been testing this possibility of exposing the UIApplication in the xru class and I got it working but it looks a bit cumbersome as a solution.

I initially just exposed the Uiapp property marking it as public here https://github.com/specklesystems/xUnitRevit/blob/e6194701cee3f9a3257ac948d0a025b60f9cfd29/xUnitRevitUtils2021/xru.cs#L19 I then was able to get the peoperty from my tests' code, but I wasn't able to use it. I was getting an exception saying it was owned by a different thread.

So I understood that I also needed to expose the UiContext property too. I did that and I'm now able to run my tests in Revit (which is wonderful in itself and I love you guys for that ❤️ ), but now my test's code looks a bit weird and I'm wondering if there's a more elegant solution to what I'm doing.

Here is an extract of my tests' fixture (where I set up the Revit Dcoument for the tests) using the public Uiapp and UiContext objects:

            // Create test document
            RevitModel model = new RevitModel();

            xru.UiContext.Send(x =>
            {
                model.CreateFromTemplate(xru.Uiapp.Application);
            }, null);

            xru.UiContext.Send(x =>
            {
                model.Document.SaveAs(saveTo);
            }, null);

            // Set project info
            xru.RunInTransaction(() =>
            {
                model.SetProjectInfo();
            }, model.Document).Wait();

Is there a better way to manage the threading without exposing the UiContext and calling it in my test? Or is this the way it is actually supposed to be?

teocomi commented 4 years ago

Well you were getting an error because some Revit methods have to be called from the same thread in use by Revit, but tests are being triggered on another thread. Hence the use of the SyncronizationContext, you could simplify your code by merging the two xru.UiContext.Send methods in one, but otherwise it looks good! I'm away today, can look at your PR upon my return!

teocomi commented 4 years ago

Also, note that xru already has a method for creating a new document from a template file xru.CreateNewDoc... You could add your other helper methods there!

Dre-Tas commented 4 years ago

Yeah I've seen the CreateNewDoc method in xru and that's what I used at the beginning to see if the tests where working, but I need to test our RevitModel class that wraps up a few base Revit functionalities and integrates them with some company-specific properties and rules.

Enjoy your time off 😉