How can I initialize TestContext property in another class? - c#

I am new to Selenium and I am trying to perform a data-driven test through CSV file. For this I am defining DataSource attribute in a class which contains test attributes. I am using MStest framework.
[TestClass]
public class UnitTest1:BaseDriver
{
ExcelTest sd;
private TestContext instance;
public TestContext TestContext
{
set { instance = value; }
get { return instance; }
}
public UnitTest1()
{
sd = new ExcelTest(_driver);
}
[TestInitialize]
public void Testinitialize()
{
}
[TestMethod]
[DeploymentItem("TestData.csv")]
[DataSource("Microsoft.VisualStudio.TestTools.DataSource.CSV", #"C:\Users\nidumukv\Documents\Visual Studio 2012\Projects\BMICalculator\BMICalculator\DataFiles\TestData.csv", "TestData#csv", DataAccessMethod.Sequential)]
public void DDtest_usingCSV()
{
string feet = TestContext.DataRow["feet"].ToString();
string inches = TestContext.DataRow["inches"].ToString();
string weight = TestContext.DataRow["weight in pounds"].ToString();
string BMI = TestContext.DataRow["BMI"].ToString();
sd.TestUsingCSV(feet,inches,weight,BMI);
}
[TestCleanup]
public void cleanup()
{ _driver.Quit(); }
}
BaseDriver is a class I am using to store the actual webdriver. PageElements is a class in which I have declared all the web elements.
I am trying to define the variables that are in 'DDtest_usingCSV' method in a seperate class so that the test does not become clumsy. But whenever I am defining another testcontext in another class I am getting a NullReferenceException. I have tried passing the property between classes. But I could not do it (I am still learning).
Below is the class I am trying to initialize the TestContext
public class ExcelTest:PageElements
{
public IWebDriver _driver;
public ExcelTest(IWebDriver driver):base(driver)
{
_driver = driver;
}
public void TestUsingCSV(string _feet,string _inches,string _weight,string _BMI)
{
feet.SendKeys(_feet);
inches.SendKeys(_inches);
weight.SendKeys(_weight);
compute_btn.Click();
}
}
As I could not initialize the property, I am parameterizing that method in the test class file.
And while declaring the TestContext property as mentioned below, why are we using "TestContext" as property name instead of instance??
private TestContext instance;
public TestContext TestContext
{
set { instance = value; }
get { return instance; }
}
At the time of reading values from excel, we are taking "TestContext" for accessing DataRow instead of "instance" . This question is bugging me whenever I look at it.
public void DDtest_usingCSV()
{
string feet = TestContext.DataRow["feet"].ToString();
string inches = TestContext.DataRow["inches"].ToString();
string weight = TestContext.DataRow["weight in pounds"].ToString();
string BMI = TestContext.DataRow["BMI"].ToString();
sd.TestUsingCSV(feet,inches,weight,BMI);
}
Please don't mind the length of the question. I gave a detailed explanation on my problem. Any help can be appreciated. Thanks in advance.

TestContext is set automatically by the MSTEST framework but only in the class attributed with [TestClass] and when it executes a test from this class.
In you case, just pass TestContext as a parameter in the TestUsingCSV method of the ExcelTest class.

Related

Do not share static properties in tests

I have to write tests for existing code with xUnit. Here is a simplified problem I faced with:
using Xunit;
namespace XUnitTestProject1
{
public class UnitTest1
{
[Fact]
public void Test1()
{
GlobalHanler.StaticProperty = "some value";
}
[Fact]
public void Test2()
{
Assert.Null(GlobalHanler.StaticProperty);
}
}
public static class GlobalHanler
{
public static string StaticProperty;
}
}
GlobalHandler owns another object, both are static
When I run 2 tests together, Test2 fails because it can see the value that was set in Test1.
However, when I run Test2 separately, it succeeds.
My goal is to make 2 tests pass when they run together. I need somehow to reset global static property for each test separately, but I can't change the implementation of GlobalHandler
Setting StaticProperty in each test method seems not an option, because it will affect other tests running in parallel
In order to have testable code, you should first put the logic in a class that can be created as many times as needed and then use that class across your code by passing the reference.
Your production code can always use a singleton, but having no direct references to the singleton makes it testable.
Singletons and testing do not cohabit very well due to the possible side effects and unclear dependencies.
You should avoid using static. Instead of this, create a simple class and register it in your DI container as a singleton. Then you can test it easily.
Simple example:
using Microsoft.Extensions.DependencyInjection;
public class Program
{
public static void Main()
{
var serviceProvider = new ServiceCollection()
.AddSingleton<IQueueUrlProvider, QueueUrlProvider>()
.BuildServiceProvider();
Console.WriteLine(serviceProvider.GetService<IQueueUrlProvider>().QueueUrl);
}
public interface IQueueUrlProvider
{
string QueueUrl { get; }
}
public class QueueUrlProvider : IQueueUrlProvider
{
private readonly Lazy<string> _getQueueUrlLazy;
public string QueueUrl => _getQueueUrlLazy.Value;
public QueueUrlProvider()
{
_getQueueUrlLazy = new Lazy<string>(GetQueueUrl);
}
private string GetQueueUrl()
{
// get url here
return "your queue url";
}
}
}
https://dotnetfiddle.net/JjRh4q

Set up Private variable in Class for Nunit Test

I'm trying to write a unit test for a class but the class has a Private variable initiated when the class is created..
public class OrderFormService : IOrderFormService
{
private readonly IOrderItems _orderItems;
private readonly string _orderStartingGroup;
// constructor
public OrderFormService(IOrderItems orderItems)
{
_orderItems = orderItems;
_orderStartingGroup = "Sales";
{
// Other Methods
}
I'm trying to write a unit test now and to test a method in this class and it utilises the variable _orderStartingGroup...
[TestFixture]
public class OrderFormServiceTests
{
private ITreatmentFormService _service;
private Mock<IOrderItems> _orderItems;
[SetUp]
public void SetUp()
{
_orderItems = new Mock<IOrderItems>();
_service = new OrderFormService(_orderItems);
}
}
Is it possible to set up the _orderStartingGroup in OrderFormServiceTest so it can be used in unit tests for testing some methods in OrderFormService? If so, how do I go about it? I've tried googling it but results keep talking about accessing private variables in the class you're testing but this isn't what I'm trying to do.
Thanks in advance :)
Well even if there is a way of setting private field directly from unit test method it’ll break an architectural principle or two..
There are a few ways of how to deal with this problem. The simplest solution would be to change the ctor signature by adding an optional parameter:
// constructor
public OrderFormService(IOrderItems orderItems, string orderStartingGroup = null)
{
_orderItems = orderItems;
_orderStartingGroup = orderStartingGroup ?? "Sales";
{
And use it in unit test:
[SetUp]
public void SetUp()
{
_orderItems = new Mock<IOrderItems>();
_service = new OrderFormService(_orderItems, “testValue”);
}
I think this might an indicator that you are doing something wrong. I would rather focus on public interfaces rather than testing internal implementation. If this somehow reflected in public interface there should be a way to set it via public interface or convention (say read from a config file).
But if you absolutely need to do that, you can consider following:
Use fabjan's answer
Set private field value with reflection
Make this private member as protected and create an derived test class.

Repeat a set of tests for different conditions?

I would like to a repeat set of unit tests, having the same setup fixture (SetUpFixture), for different conditions.
i.e. set of tests use the same file as an input. The file is being set in the setup fixture. I would like to repeat the tests that use the set up fixture for different input file.
It's possible for TextFixture, but not for SetupFixture. Is there a workaround for this?
What you can do is to generate few input files and use the file name as a parameter for your test:
[TestFixture]
public class MyClass
{
[TestFixtureSetUp]
//[OneTimeSetUp] for NUnit 3
public void FixtureSetUp()
{
PrepareFile("a.txt");
PrepareFile("b.txt");
PrepareFile("c.txt");
}
[TestCase("a.txt")]
[TestCase("b.txt")]
[TestCase("c.txt")]
public void Test(string fileName)
{
var result = YourTestCode(fileName);
Assert.True(result); //whatever you need
}
}
You could use the TestFixtureAttribute to construct your test class with each data file. Migrating Anton's answer;
[TestFixture("a.txt")]
[TestFixture("b.txt")]
[TestFixture("c.txt")]
public class MyClass
{
string _filename;
public MyClass(string filename)
{
_filename = filename;
}
[TestFixtureSetUp]
//[OneTimeSetUp] for NUnit 3
public void FixtureSetUp()
{
PrepareFile(_filename);
}
[Test()]
public void Test()
{
var result = YourTestCode();
Assert.True(result); //whatever you need
}
}
If you want a more dynamic way you provide the data for the TestFixture, look at the TestFixtureSourceAttribute

IntelliSense do not work with extension method when property name is the same as class name

Based on this answer: " Should a property have the same name as its type? ", I've started to use property names the same as their class names. But recently I've met a strange corner case and I don't know if it is only my problem and how to solve it. Here is the code to repeat the case:
class R
{
public Test Test { get; private set; }
public R()
{
Test = new Test();
// IntelliSense not working here:
// Test.Use(
}
}
public class Test
{
}
public static class Extensions
{
public static void Use(this Test test, string msg)
{
Console.WriteLine(msg);
}
}
I'm using VS2010 and .NET Framework 4.0
Here is the video showing the problem: http://www.youtube.com/watch?v=HgszAu_Pir0&feature=youtu.be
Could you try using .this when selecting the property?
eg. this.Test.use() ..

VS Team Test: Multiple Test Initialize Methods in Test Class

I have unit test project called “MyClassTest” in TeamTest. This project has three TestMethods. Each method needs its own test initialization steps. But when I apply TestInitializeAttribute to three initialization methods, it says the attribute should not be used more than once. Then what should be the attribute to be used to initialize each test method in Visual Studio Team Test?
Reference:
VS Team Test: .Net Unit Testing with Excel as Data Source: Adapter Failed
How to create Startup and Cleanup script for Visual Studio Test Project?
VS 2010 Load Tests Results with custom counters
How to log unit test entry and leave in MSTest
Can a unit test project load the target application's app.config file?
According to MSDN the TestInitializeAttribute:
cannot be used more than once (AllowMultiple = false), and
cannot be inherited to create your own TestInitializeAttribute.
So, my suggestion is to create the Test Initialize Methods without the TestInitialize attribute. Then in the unique TestInitialize method check which is the current executed TestMethod and call the appropriate initialize method:
[TestClass]
public class UnitTest
{
public TestContext TestContext { get; set; }
[TestInitialize]
public void Initialize()
{
switch (TestContext.TestName)
{
case "TestMethod1":
this.IntializeTestMethod1();
break;
case "TestMethod2":
this.IntializeTestMethod2();
break;
default:
break;
}
}
[TestMethod]
public void TestMethod1()
{
}
[TestMethod]
public void TestMethod2()
{
}
public void IntializeTestMethod1()
{
//Initialize Test Method 1
}
public void IntializeTestMethod2()
{
//Initialize Test Method 2
}
}
If you have three test methods, and each method has its own initialization steps, then why are you moving initialization to method which will run before every test? Only benefit I see, is that nice switch block, which adds some lines to your source file. But it gives you drawback - looking on any of these test methods, you can't really tell in which context method will be executed. So, I use initialization method to setup only basic context, which is really used by all tests in fixture.
Just move context creation to arrange part of each method.
If you have several methods, which use common context, then just extract method, which will setup context for them, and call it at the arrange part. You also can split each context setup to several steps and reuse those steps (like it done in Given-When-Then tools like Specflow).
And, of course, creating different fixtures also option.
It's a bit of an old post, but I came up with the following which seems to work OK:
First, define an attribute class:
[AttributeUsage(AttributeTargets.Method, Inherited = true)]
public class InitialiseWithAttribute : Attribute
{
public string Id { get; private set; }
public InitialiseWithAttribute(string id)
{
Id = id;
}
}
then define an extension method in some convenient utilities class:
public static bool IsInitialisedWith(this string testName, string value)
{
bool result = false;
Type testClassType = new StackFrame(1).GetMethod().DeclaringType;
MethodInfo methodInfo = testClassType.GetMethod(testName);
if (methodInfo != null)
{
InitialiseWithAttribute initialiseWithAttribute =
methodInfo.GetCustomAttribute<InitialiseWithAttribute>(true);
if (initialiseWithAttribute != null)
{
result = initialiseWithAttribute.Id == value;
}
}
return result;
}
Now write your tests, thus:
public TestContext TestContext {get; set;}
[TestInitialize]
public void TestInitialise()
{
if (TestContext.TestName.IsInitalisedWith("DoSomethingSpecial")
{
// ... Do something special
}
else
{
// ... Do something normal
}
}
[TestMethod]
[InitialiseWith("DoSomethingSpecial")]
public void MySpecialTest()
{
// The test
}
If they need three seperate inits; then they should probably be in three separate fixtures each with their own init!
At my job we pass in an argument to TestInitialize method to determine how we want initialization to work.
public partial class CommonActions
{
public void TestInitialize(bool adminTest)
{
try
{
if (adminTest)
{
//do stuff
}
We then have a standard initialization in class definition, which defaults to false.
[TestClass]
public class ProjectTestBase : FrameworkTestBase
{
public CommonActions common { get; set; } = new CommonActions();
[TestInitialize]
public void TestInitialize() => common.TestInitialize(false);
Then in the Test cases themselves you can override the TestInitialize for any test you want.
[TestClass]
public class SetReportsInAdmin : ProjectTestBase
{
[TestInitialize]
public new void TestInitialize() => common.TestInitialize(true);
We use a Boolean to tell if Admin test, which needs to have extra overhead for setup. Take this and apply whatever variables you want in a way the gives you multiple initialization through the use of one method.

Categories