2

In our application, I have created a login test for our website. If this test is successful then login is successful. Now to create tests for other parts of the website, I need to perform login first.

Is there a way so that all the other tests that I will create will run the login test first? For example: is it possible to create a custom login test attribute that I can put on all other test methods, so that they will run login first? But then I also need the return value from that login test !

If not, then I will have to just write a plain C# function that performs login operation and I will need to put that function call, as the first line of every test.

Varun Sharma
  • 2,591
  • 8
  • 45
  • 63
  • Just put your login logic in a method and use the `TestInitialize` attribute to automatically run it before other tests. https://msdn.microsoft.com/en-us/library/ms182517(v=vs.100).aspx – Kevin Gosse Mar 13 '16 at 23:19
  • But it returns some objects that I need to use in all other tests. How will I get that ? – Varun Sharma Mar 13 '16 at 23:20
  • Test should not return something and should not depend on each other, just put whole logic in a method then call the method in both tests. – Valentin Mar 13 '16 at 23:39

3 Answers3

4

When you're writing unit tests, you are testing each method in isolation. Meaning, the method that you're testing must not, and cannot depend on anything else in order to run.

The reason why you have a problem now, probably is because you broke at least the single responsability principle (the one that states that your class should only have one single reason to be changed). I can assume this because you are stating that your OTHER methods in a class depend on login being successful. Here's how to solve it:

Make an interface for your login method, something like:

public interface ILoginManager{
   void Authenticate(string username, string password);

   void IsAuthenticated{ get;}
}

Next, using the Dependency Inversion Principle, add this interface to your class with methods in it:

public class MyWorkerClass
{
  private readonly ILoginManager _loginManager;
  public MyWorkerClass(ILoginManager loginManager){
    _loginManager = loginManager;
  }


  public bool LogOnUser(string userName, string password){
    _loginManager.Authenticate(userName, password);

    return _loginManager.IsAuthenticated;
  }
}

Now, in all your tests, you can mock out the LoginManager, and set your expectations there, i.e.

[TestMethod]
public void SomeMethod_UserIsAuthenticated_InvokesSomeOtherMethod()
{
  // Arrange
  GetMockFor<ILoginManager>().SetupGet(lm => lm.Authenticated).Returns(true);

  // Act
  var result = Instance.SomeMethod();

  // Assert
  GetMockFor<ISomeOtherInterface>()
    .Verify(o => o.SomeOtherMethod(), Times.AtLeastOnce() );
}
Pedro G. Dias
  • 3,162
  • 1
  • 18
  • 30
4

What about a base class?

[TestClass]
public class AuthenticatedTest
{
  [TestInitialize]
  public void TestInitialize()
  {
    // login
  }
}

[TestClass]
public class MyTests : AuthenticatedTest
{
  [TestMethod]
  public void Whatever()
  {
    // already logged in.
  }
}

You should not write tests which rely on other tests. If you need to log in before, you have to log n before, not "run the login test". It is probably not much difference of the code, but of the concept.

Stefan Steinegger
  • 63,782
  • 15
  • 129
  • 193
1

Sometimes you need to start the web server before such tests, especially if it's a WCF service. I tried all sorts of Cassini / IIS Express logic before I realized that it's only the local ASP.NET server that needs to be running:

static System.Diagnostics.Process server = null;

public static void RunSite()
{
    string shared = @"C:\Program Files (x86)\Common Files\Microsoft Shared";
    string aspServer = shared + @"\DevServer\10.0\WebDev.WebServer40.EXE";
    string webSite = @"C:\Code\YourProject\WebSite";
    int port = 1234;
    var startInfo = new System.Diagnostics.ProcessStartInfo(aspServer);
    startInfo.Arguments = "/port:" + port + " /path:" + webSite;
    server = System.Diagnostics.Process.Start(startInfo);
}


[AssemblyInitialize]
public static void AssemblyInit(TestContext context)
{
    RunSite();
    // login ...
}

[AssemblyCleanup]
public static void AssemblyCleanup()
{
    server.Kill();
}

NOTE: This might leave WebDev.WebServer40.EXE running, if you don't let a test debug session complete the Assembly Cleanup. In that case, you can shut down the little yellow web page icon from the System Tray.

CZahrobsky
  • 762
  • 7
  • 7