How do I get a reference to the application under test? - c#

I'm using specflow with the NUnit test runner. When I write my feature file and ask specflow to generate the steps, it outputs the following code:
using System;
using TechTalk.SpecFlow;
using Xamarin.UITest.Android;
namespace UITest1
{
[Binding]
public class CategoryPagerSteps
{
[Given(#"The (.*)st category is selected")]
public void GivenTheStCategoryIsSelected(int p0)
{
ScenarioContext.Current.Pending();
}
[When(#"I swipe left")]
public void WhenISwipeLeft()
{
ScenarioContext.Current.Pending();
}
[Then(#"The (.*)nd category is selected")]
public void ThenTheNdCategoryIsSelected(int p0)
{
ScenarioContext.Current.Pending();
}
}
}
This is fine, and I understand that these are "Steps" which will be called when my cucumber file with scenarios written in Gherkin calls for them.
However, being that this is a fully-integrated UI test, I need to be able to use Xamarin.UITest.Android to click on views and such.
So I need to somehow grab the object that represents the application that is under test so I can perform UI operations on it.
Now, I can see that this object is being initialized in another auto-generated test fixture file called "Tests.cs":
using NUnit.Framework;
using Xamarin.UITest;
using Xamarin.UITest.Android;
namespace UITest1
{
[TestFixture]
public class Tests
{
AndroidApp app;
[SetUp]
public void BeforeEachTest()
{
// TODO: If the Android app being tested is included in the solution then open
// the Unit Tests window, right click Test Apps, select Add App Project
// and select the app projects that should be tested.
app = ConfigureApp
.Android
// TODO: Update this path to point to your Android app and uncomment the
// code if the app is not included in the solution.
//.ApkFile ("../../../Android/bin/Debug/UITestsAndroid.apk")
.StartApp();
}
[Test]
public void AppLaunches()
{
app.Screenshot("First screen.");
}
}
}
I can see that the property AndroidApp app is the object that I need access to, but how do I access that property from the CategoryPagerSteps code above? Tests is not static nor are any of the methods or properties. I'm nervous to simply instantiate it myself because that should probably be done by the test runner, right? One of the other auto-generated files contains a testRunner property, but it is marked private.
So every avenue I've gone down appears blocked and I feel that I'm missing something obvious.

Here's how I solved it, in case anyone else might find it useful:
Following up on the link provided by #CheeseBaron from arteksoftware, the trick is to use SpecFlow's FeatureContext.Current to hold the value. This is one of the intended uses of FeatureContext.
The reference from arteksoftware used this method, as shown in this code:
[SetUp]
public void BeforeEachTest ()
{
app = AppInitializer.StartApp (platform, iOSSimulator);
FeatureContext.Current.Add ("App", app);
//This next line is not relevant to this post.
AppInitializer.InitializeScreens (platform);
}
However, it didn't work immediately for me because the [Setup] binding would not be called as part of a specflow test. Changing the binding to the SpecFlow [BeforeFeature] binding and making the method static solved the problem.
[BeforeFeature]
public static void Before()
{
AndroidApp app;
Console.WriteLine("** [BeforeFeature]");
app = ConfigureApp
.Android
// TODO: Update this path to point to your Android app and uncomment the
// code if the app is not included in the solution.
.ApkFile(<Path to APK>)
.StartApp();
FeatureContext.Current.Add("App", app);
}
Then, in the feature code itself, the app could be extracted from the FeatureContext dictionary like so:
[Binding]
public class FeatureSteps
{
AndroidApp app;
public FeatureSteps()
{
app = FeatureContext.Current.Get<AndroidApp>("App");
}
//Code for the rest of your feature steps.
}
I imagine that the selection of one's test runner is relevant to the bindings that are used, so here's my "App.config". I'm using NUnit with a SpecFlow plugin. I didn't try it with other test runner configurations.
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<section name="specFlow" type="TechTalk.SpecFlow.Configuration.ConfigurationSectionHandler, TechTalk.SpecFlow" />
</configSections>
<specFlow>
<!-- For additional details on SpecFlow configuration options see http://go.specflow.org/doc-config -->
<!-- For additional details on SpecFlow configuration options see http://go.specflow.org/doc-config -->
<!-- use unit test provider SpecRun+NUnit or SpecRun+MsTest for being able to execute the tests with SpecRun and another provider -->
<unitTestProvider name="NUnit" />
<plugins>
<add name="SpecRun" />
</plugins>
</specFlow>
</configuration>

The repo mentioned above https://github.com/RobGibbens/BddWithXamarinUITest last updated 2015 and things have moved on to POP architecture, which I would recommend.
Have a look here - POP architecture with SpecFlow, https://github.com/xamarin-automation-service/uitest-specflow-example
You can see there is a static class called AppManager which has the App property
There a lot of videos and blog post on this style:
https://www.youtube.com/watch?v=4VR861BWkiU
https://www.youtube.com/watch?v=i0UNQvfXZhM
https://xamarindevelopersummit.com/page-object-pattern-and-uitest-best-practicies/
https://devblogs.microsoft.com/xamarin/best-practices-tips-xamarin-uitest/

Related

C# Is it possible to run nunit tests inside ASP.Net?

I have a very simple question.
Is it possible to run Nunit tests inside an asp.net web app?
Here is an example project:
MyApp -> ASP.Net app (.net5)
MyTests -> Nunit Tests (.net5)
My Asp.net project (MYApp) contains all my controllers and such, with a depency on NUnit.Engine and my test project.
There is another Test project (MyTests), which is just a dummy project.
I want to be able to run in a controller, inside my web app, my tests.
Example controller:
namespace MyApp.Controllers
{
[Route("api/tests")]
[ApiController]
public class TestController: ControllerBase
{
// Some helper class to verify everything is working somehow
private class ReportListener : ITestEventListener
{
public void OnTestEvent(string report)
{
Console.WriteLine(report);
}
}
[HttpGet]
public async Task<ActionResult> Trigger()
{
try
{
using ITestEngine engine = TestEngineActivator.CreateInstance();
engine.Initialize();
engine.WorkDirectory = Path.Combine(Directory.GetCurrentDirectory(), "../","MyTests/");
// Create a simple test package - one assembly, no special settings
TestPackage package = new TestPackage(#".\bin\Debug\net5.0\MyTests.dll"); //Just for debugging and testing
// Get a runner for the test package
using ITestRunner runner = engine.GetRunner(package);
runner.Load();
// Run all the tests in the assembly
XmlNode testResult = runner.Run(listener: new ReportListener(), TestFilter.Empty);
var outputs = Enumerable.Empty<string>();
foreach (XmlNode elem in testResult.SelectNodes("//test-case/output"))
{
outputs = outputs.Append(elem.InnerText);
}
}catch(Exception e)
{
}
return Ok();
}
}
}
But unfortunately all my attemps so far have failed.
Am I missing something?
Is Nunit.Engine not made to be run in an asp.net context?
I am building all this in .NET5.0 (company policy)
If needed I can provide an example project
There could be more, but one small thing would explain the failure...
The NUnit engine defaults to running tests in a separate process, which it launches. So, assuming that your code is working correctly as written, a ProcessRunner will be created and the engine will communicate with it, telling it to run your tests.
This could fail in one of two ways:
You may not have permission to create a process.
If you succeed in creating it, the code will definitely not be running in the asp.net context. In that case, it would probably error out and terminate with very little debug information provided.
A simple fix is to add a setting to the test package, telling the engine to run the tests in process.
TestPackage package = new TestPackage(#".\bin\Debug\net5.0\MyTests.dll");
package.AddSetting("ProcessModel", "InProcess");
If you get a second error after doing this, it should at least result in a clearer message and you should be able to debug through the code.

NUnit TestFixtures do not work with RestSharp

I'm trying to use NUnit TestAttributes to create and delete a RestSharp RestClient
https://github.com/nunit/docs/wiki/TestFixture-Attribute
using NUnit.Framework;
using RestSharp;
namespace Sanitized.Sanitized.Steps
{
[TestFixture]
public class SetupAndTeardown
{
public RestClient restClient;
[SetUp]
public void Init()
{
restClient = new RestClient();
}
[TearDown]
public void Cleanup()
{
restClient = null;
}
}
}
But, I get the error Object reference not set to an instance of an object. when trying to use this in another class i.e. with my automated steps.
I don't understand this, as I thought code that's in the [SetUp] [Teardown] attributes are called at the beginning and end of the test respectively.
You created a TestFixture, which is a class that contains tests. If the fixture had any tests, then NUnit would run them and would also run the setup before each test and the teardown after each test. Since you have no tests, that isn't happening. NUnit recognizes the fixture but finds nothing to run there.
You say that you have a problem when you "use" this fixture in another class. Test fixtures are not intended to be "used" by other code. Rather, they are run by NUnit.
For a better answer about how to do what you are trying to do, we first need to know what you are trying to do. When do you want the "setup" and "teardown" to run? How often should they run? Depending on those things, I can update this answer.
Replying further to your comment... If your tests are in another class, then that class is your test fixture. Is there a reason you don't want it to be the fixture?

How to configure unit test name/description on VSTS?

I have a solution that contains a unit test project. I'm using NUnit v3.10.1 and NUnit3TestAdapter v3.10.0.
Is there a way to configure how the test names are displayed in Visual Studio Team Services (VSTS), maybe displaying the test class name? At the moment it displays only the test name:
It's hard to understand which test belongs to which class. In this case, I have at least 2 test classes that have the same test names.
Running the same tests using Reshaper's test runner it's quite easy to understand which tests belong to which classes:
I have tried settings the TestName of the TestFixture Attribute attribute or setting the Description of the Test Attribute with no luck:
[TestFixture(TestName = "MemoryCacheManagerTests_TryGetItem", TestOf = typeof(MemoryCacheManager))]
public class MemoryCacheManagerTests_TryGetItem : MemoryCacheManagerTests
{
[Test(Description = "MemoryCacheManagerTests_TryGetItem_WithInvalidCacheKey_ShouldThrowArgumentException")]
[TestCaseSource(typeof(InvalidCacheKeyTestCases))]
public void WithInvalidCacheKey_ShouldThrowArgumentException(string key)
{
// ...
}
}
So how do I configure the names of the tests on VSTS?
No, there's no way to configure it. You can submit a UserVoice item to suggest product improvements.

Is it possible to use two different Owin startup file at run time?

I am using an Asp.net MVC 5 app with MEF framework to allow me to design MVC app as a plugin to in the main app.
I have a need where one of my plugin needs to have its own OwinStart up class that runs after the the master Owin class which belongs to my main app.
In other words main.dll has Startup class which always needs to run first, then plugin.dll has a Startup class that needs to run second.
Is it possible to have 2 Own Startup classes?
From the Docs about detecting the StartUp class
The OwinStartup attribute overrides the naming convention. You can
also specify a friendly name with this attribute, however, using a
friendly name requires you to also use the appSetting element in the
configuration file.
So I tried adding a friendly name like so
[assembly: OwinStartup("pluginStartup", typeof(plugin.Startup))]
The added the following in the config file
<appSettings>
<add key="owin:appStartup" value="Main.Startup, Main" />
</appSettings>
But that does not file my Plugin.Startup it only runs Main.Startup.
Is there a way to run two different Startup classes?
https://learn.microsoft.com/en-us/aspnet/aspnet/overview/owin-and-katana/owin-startup-class-detection
It does not seems possible to run multiple startup files.
However, I used reflection to get the job done. Basically, I search all assemblies for any class the implements a IAppConfiguration interface, then call the Configuration on that instance.
Here is how I did it.
I created an interface
public interface IAppConfiguration
{
void Configuration(IAppBuilder app);
}
Then in my main.dll I added the following code to my Startup class.
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
ConfigurePlugins(app);
}
private static void ConfigurePlugins(IAppBuilder app)
{
try
{
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
{
var startups = assembly.GetTypes().Where(x => x.IsClass && typeof(IAppConfiguration ).IsAssignableFrom(x)).ToList();
foreach (Type startup in startups)
{
var config = (IAppConfiguration )Activator.CreateInstance(startup);
config.Configuration(app);
}
}
}
catch
{
}
}

NUnit in vs2012 reusing app.config when i click "Run All Tests" on multiple project solution?

I have a visual studio 2012 solution with 2 projects projA and projB, and 2 test projects projA_test and projB_test (using NUnit). ProjA and ProjB both have their own app.config with their own appsettings key/value pairs. When I click the run all tests the projA unit-tests are ran followed by the projB unit-tests. But some of the projB unit tests are failing because of ConfigurationManager.AppSettings has only the values from the app.config from projA! Is this normal behaviour or do I have some configuration setting messed up?
Edit.
A point I forgot to mention is that TeamCity (our CI server) does not fail the tests. So my local unit-test runner seems to be doing something weird..
For each of your unit tests you can just set the values for the ConfigurationManager as such.
ConfigurationManager.AppSettings["YourKey"] = YourValue;
If you do this before you objects that require those values they will work just fine. If they values are for multiple test you can do it in the testfixture setup method.
Detailed Example
Class:
public class SomeClass
{
#region Properties
public string Value { get; private set; }
#endregion Properites
#region Constructors
public SomeClass()
{
Value = ConfigurationManager.AppSettings["DatValue"];
}
#endregion Constructors
}
Test: I know this is MSTest/FluentAssertion but concept should work the same
[TestMethod]
public void Example()
{
#region Arrange
ConfigurationManager.AppSettings["DatValue"] = "Batman";
#endregion Arrange
#region Act
var someClass = new SomeClass();
#endregion Act
#region Assert
someClass.Should().NotBeNull();
someClass.Should().BeOfType<SomeClass>();
someClass.Value.Should().BeEquivalentTo("Batman");
#endregion Assert
}
This test passes in the example I just made and has no app.config file available. A unit test shouldn't be dependent on an app.config otherwise you start creepying from unit test to integration testing.
A note that ConnectionStrings are a little different. You would add those to the ConfigurationManager as such:
ConfigurationManager.ConnectionStrings.Add(new ConnectionStringSettings("Name", "ConnectionString"));

Categories