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.
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() );
}
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.
Related
On the xUnit website it says the following about constructor:
xUnit.net creates a new instance of the test class for every test that
is run, so any code which is placed into the constructor of the test
class will be run for every single test. This makes the constructor a
convenient place to put reusable context setup code where you want to
share the code without sharing object instances (meaning, you get a
clean copy of the context object(s) for every test that is run).
I have the following code:
public class ProfilePageTest
{
public ProfilePageTest(Role role)
{
AuthRepository.Login(role)
}
[Theory]
[Roles(Role.Editor, Role.Viewer)]
public void OpenProfilePageTest(Role role)
{
var profile = GetPage<ProfilePage>();
profile.GoTo();
profile.IsAt();
}
}
Is it possible to pass the role from the theory attribute to the constructor, so I don't have to do AuthRepository.Login(role) in every test method that I have.
No, that's not possible. The constructor will be run before anything else, as with any constructor you're used to. I don't see the harm in calling AuthRepository.Login(role) in every test though, because it's a single line of code.
This is quite an excellent blog post about the different ways you can pass data into xUnit tests, but all of them are passing in data to individual methods (tests) rather than in the constructor.
If you are looking to set something up for multiple tests you should have a look int IClassFixture
Quick run down, you setup a class with the shared data:
public class DatabaseFixture : IDisposable
{
public DatabaseFixture()
{
Db = new SqlConnection("MyConnectionString");
// ... initialize data in the test database ...
}
public void Dispose()
{
// ... clean up test data from the database ...
}
public SqlConnection Db { get; private set; }
}
And then in your tests you can "inject" the class (along with the data) into the test class:
public class MyDatabaseTests : IClassFixture<DatabaseFixture>
{
DatabaseFixture fixture;
public MyDatabaseTests(DatabaseFixture fixture)
{
this.fixture = fixture;
}
// ... write tests, using fixture.Db to get access to the SQL Server ...
}
Ok, I've got some nunit tests I'm writing to test an API. Any time I need to run these tests, I first need to login to the api to obtain a token. To start with, that's how I've written my OneTimeSetUp.
So, OneTimeSetUp is called, I log in, a shared field stores the token, each test is called a tests a different endpoint on api.
Now the problem. We've decided that we want to have individual tests for individual fields on the response, so that we can see what exactly is (and isn't failing) if something is wrong. So, we split out each endpoint into it's own test.
Now, OneTimeSetUp is called, it logs in, and calls the endpoint, stores the result, and all the tests fire, testing their little bit.
The problem is, logging in takes time, and there is no logical reason why all the separate tests couldn't just use the same login details. Is there any way of further sub-dividing tests/ adding extra levels of test? It would be great if we could get a test result that looks like this
ApiTests <--- shared sign-in at this level
- Endpoint 1 <--- call the endpoint at this level
- Field 1 \
- Field 2 --- individual test results here
- Field 3 /
- Endpoint 2 <--- call the endpoint at this level
- Field a \
- Field b --- individual test results here
- Field c /
You can group your test classes into the same namespaces and then add an additional class that is marked with the SetupFixture attribute. This will run the initialization code only once per namespace. (Not to be confused with the "TestFixtureSetUp" attribute, which is marked obsolete since NUnit v3. Thanks Charlie for your comment, I initially mixed it up.)
https://github.com/nunit/docs/wiki/SetUpFixture-Attribute
Code sample (as always, you are free to put each class into a separate code file):
using System.Diagnostics;
using NUnit.Framework;
namespace Test
{
[SetUpFixture]
public class SharedActions
{
[OneTimeSetUp]
public void SharedSignIn()
{
Debug.WriteLine("Signed in.");
}
[OneTimeTearDown]
public void SharedSignOut()
{
Debug.WriteLine("Signed out.");
}
}
[TestFixture]
public class FirstEndpointTests
{
[Test]
public void FirstEndpointTest()
{
Debug.WriteLine("Test for Endpoint A");
}
}
[TestFixture]
public class SecondEndpointTests
{
[Test]
public void SecondEndpointTest()
{
Debug.WriteLine("Test for Endpoint B");
}
}
}
When you "debug all" tests, the following output will appear in the debug window:
Signed in.
Test for Endpoint A
Test for Endpoint B
Signed out.
Here is one possible way of achieving this.
If you have a common base class (as it sounds from your description), you can create a protected lazy to get your token as per the example below
public class ApiTestsBase
{
protected static Lazy<string> TokenLazy = new Lazy<string>(() =>
{
// Log in and get your API token
Console.WriteLine("Logging into API to get token. You should only see this message on the first test that runs");
return "DEADBEEF";
});
}
[TestFixture]
public class EndpointATests : ApiTestsBase
{
private string GetResultFromEndPoint()
{
// Call endpoint with token from TokenLazy.Value
Console.WriteLine($"Calling EndpointA with token {TokenLazy.Value}");
return "PayloadA";
}
[Test]
public void Test1()
{
var payload = this.GetResultFromEndPoint();
// Assert things about payload
}
}
[TestFixture]
public class EndpointBTests : ApiTestsBase
{
private string GetResultFromEndPoint()
{
// Call endpoint with token from TokenLazy.Value
Console.WriteLine($"Calling EndpointB with token {TokenLazy.Value}");
return "PayloadB";
}
[Test]
public void Test1()
{
var payload = this.GetResultFromEndPoint();
// Assert things about payload
}
}
Now I am using string types, but you can use whatever request, response and token types are relevant to your situation. I suspect that you could also with a bit of creativity move the GetResultFromEndPoint call to the base class and use abstract methods or properties to fill in the endpoint specific detail, but you have not shared enough code for me to try that.
The magic is in the static keyword which means you will only have one instance per app domain. The Lazy simply defers creation until its first reference. It gets a little more complex if your test cases run for a long time because you will need to deal with token renewal, but it can still be achieved in a similar way using a singleton class that periodically re authenticates if token age > x. A singleton object can also be used in place of the static in the above example if you do not have a common base class for your fixtures.
I am testing the following controller:
public class MyController : ApiController
{
private readonly IUserManager _users;
private int num = 0;
public MyController(IUserManager users)
{
_users = users;
}
public User GetByCredentials(string username, string pass)
{
var account = new Account(username, pass);
User u = _users.GetByCredentials(num, account);
return u;
}
I was thinking to mock the IUserManager.GetByCredentials method, as I only want to see that MyController.GetByCredentials method works as expected. The problem is that the User class cannot be instantiated directly, so I cannot mock a User object, because the constructor is private:
public class User
{
// private attributes here
protected User()
{
// do a lot of stuff here, call other objects to initialize attributes
}
private User(int num, string id, string userid, Account account)
: this()
{
// do other stuff here with the params
}
public static User CreateUser(int num, string id, string userid, Account account)
{
return new User(num, id, userid, account);
}
// and so on
}
I am using the Moq framework, but I am open to different solutions. I would prefer to avoid the creation of test data in this case, because it depends on database the initialization, servers and so on - and then it would not be a unit test anymore. Have you ever had an issue like this? How do you deal with it? Thanks.
You don't need to mock User - you can use the real User class. You only need to mock (or fake) IUserManager. Your mock/fake IUserManager can use User.CreateUser to create the User objects to be returned to your controller.
Unless the User class itself "knows" about the database, that should be fine. Resist the temptation to mock everything - you only need to get rid of dependencies that make it hard to write tests.
Now you've written that your User private constructor "does a lot of stuff" - if that reaches out too far, you should redesign User so it's simpler... your user manager should possibly be responsible for some of the work that's going on there.
While it's an oversimplification to completely split the world into services (like the user manager) and dumb data objects (like the user), it makes things much easier if a design reasonably naturally splits itself in that respect.
In order to test your MyController, you will be mocking IUserManager not User so you will be able to do something like this:
var mockUserManager = new Mock<IUserManager>();
// Configure the User to be returned by calling GetByCredentials
mockUserManager
.Setup(x => x.GetByCredentials(It.IsAny<int>(), It.IsAny<Account>())
.Returns(User.CreateUser(1, "foo", "username", new Account());
var controller = new MyController(mockUserManager.Object);
var user = controller.GetByCredentials("username", "password");
Assert.NotNull(user);
mockUserManager.Verify(x => x.GetByCredentials(It.IsAny<int>(), It.IsAny<Account>(), Times.Once());
The point of mock/fake objects is to avoid database/web service calls. Your Account and User classes should be poco classes - e.g. only contain methods and properties which act upon itself, no database or web service calls etc so they don't actually need mocking.
Given I have a controller class as such:
public class ResourceController : AuthorizedController
{
public virtual string Resource()
{
//do magic
}
public virtual string ResourceParent()
{
var url = Resource();
return url;
}
}
}
With the test harness:
[Subject(typeof (ResourceController))]
public class When_I_want_the_parent_resource : WithSubject<ResourceController>
{
private static readonly string ParentUrl = "/organizations/1";
private static readonly string ResourceUrl = "/organizations/1/contacts/1";
private static string _result;
private Establish context = () =>
{
The<ResourceController>()
.WhenToldTo(x => x.Resource())
.Return(ResourceUrl);
};
private Because of = () => _result = Subject.ResourceParent();
private It should_match_the_expected_parent_url = () =>
_result.ShouldEqual(ParentUrl);
}
This unit test will fail because Subject.ResourceParent() will return null because Machine.Fakes has automocked this method. As a temporary workaround I just removed the virtual keyword from ResourceParent to be able to test my code. I assume there has to be a real solution for me to tell Machine.Fakes to not override ResourceParent()
actually there isn't a "real" solution in Machine.Fakes for this. I would argue that you need to reconsider you're fixture design.
First of all, as Tim already pointed out, you you should't fake any methods on the subject itself. Instead you should fake the dependencies of your controller and use your controller as-is in specifications. The intended usage of the "The" method is to access the dependencies of the subject under specification, but what you try in your spec is to access the subject itself. I think that's where things go wrong. The <ResourceController> and subject are actually separate instances. That's why you configured interaction doesn't happen.
Just some options to fix this in your code:
Introduce a new dependency in your code which you can stub via "The"
If the first seems to heavyweight, you could also replace the virtual method with a Func<string> dependency for your controller and use the "Configure" method to inject a specification specific one for your spec.
HTH,
Bjoern
If you're testing a controller, then perhaps you shouldn't be creating mocks or fakes of that controller. Otherwise you're just testing a mock and the test doesn't really have any validity.
Mock or fake the dependencies of the controller. Test the real controller.
In the end I ended up pulling this test out of Machine.Fakes and put it into a regular unit test. Then I just used Moq directly to configure the HttpRequestBase to seed data I needed and then just manually created my controller class and invoked methods on it.
Here's what I have in my test so far:
[TestFixture]
public class IndividualMovieTests
{
[Test]
public void WebClient_Should_Download_From_Correct_Endpoint()
{
const string correctEndpoint = "http://api.rottentomatoes.com/api/public/v1.0/movies/{movie-id}.json?apikey={your-api-key}";
ApiEndpoints.Endpoints["IndividualMovie"].ShouldEqual(correctEndpoint);
}
[Test]
public void Movie_Information_Is_Loaded_Correctly()
{
Tomato tomato = new Tomato("t4qpkcsek5h6vgbsy8k4etxdd");
var movie = tomato.FindMovieById(9818);
movie.Title.ShouldEqual("Gone With The Wind");
}
}
My FIndMovieById method goes online and fetches a JSON result, and that means it sort of breaks the principle behind unit testing. I have a feeling I have to mock this string response, but I don't really know how to approach this.
How would you approach this particular unit testing?
In your second [Test], I would suggest not focusing on a specific return value from your FindMovieById method, unless you truly want to test that your given inputs should always result in "Gone With the Wind". The test that you have seems to be a very specific test case in which a specific input number results in a specific output, which is something that may or may not change when running against your actual database. Also, since you're not going to be testing against the actual web service, doing this kind of validation is basically self-serving - you're not really testing anything. Instead, focus on testing how the Tomato class handles validation of the argument (if at all), and that the Tomato class actually invokes the service to get the return value. Rather than testing specific inputs and outputs, test the behavior of the class, so that if someone changes it in the future, the test should break to alert them that they may have broken working functionality.
For example, if you have input validation, you could test that your Tomato class throws an exception if an invalid input is detected.
Assuming that your Tomato class has some sort of web client functionality for requesting and retrieving the results, you could plug in some stub implementations of the actual web code, or mocked implementations to ensure that Tomato is in fact calling the appropriate web client code to request and process the response.
First off, you might not have to mock to test you code. For example, if you are just testing that you can deserialize JSON into a Movie object, you could just do that by testing a public or internal ParseJSON recond on the Movie class.
However, since you are asking about mocking, here's a quick overview of one way you could write this test using a mock. As it is written, Movie_Information_Is_Loaded_Correctly() looks like an integration test. To turn this into a unit test, you could mock out the web request the Tomato class makes. One way to do that would be to create a ITomatoWebRequester interface and pass that as a parameter to the Tomato class in the constructor. You could then mock the ITomatoWebRequester to return the web response you are expecting, and then you could test that the Tomato class properly parses that response.
The code could look something like this:
public class Tomato
{
private readonly ITomatoWebRequester _webRequester;
public Tomato(string uniqueID, ITomatoWebRequester webRequester)
{
_webRequester = webRequester;
}
public Movie FindMovieById(int movieID)
{
var responseJSON = _webRequester.GetMovieJSONByID(movieID);
//The next line is what we want to unit test
return Movie.Parse(responseJSON);
}
}
public interface ITomatoWebRequester
{
string GetMovieJSONByID(int movieID);
}
To test, you could use a mocking framework like Moq to create a ITomatoWebRequester that will return a result you expect. To do that with Moq the following code should work:
[Test]
public void Movie_Information_Is_Loaded_Correctly()
{
var mockWebRequester = new Moq.Mock<ITomatoWebRequester>();
var myJson = "enter json response you want to use to test with here";
mockWebRequester.Setup(a => a.GetMovieJSONByID(It.IsAny<int>())
.Returns(myJson);
Tomato tomato = new Tomato("t4qpkcsek5h6vgbsy8k4etxdd",
mockWebRequester.Object);
var movie = tomato.FindMovieById(9818);
movie.Title.ShouldEqual("Gone With The Wind");
}
The cool thing about the mock in this case is that you don't have to worry about all the hoops the actual ITomatoWebRequester has jump through to return the JSON it is supposed to return, you can just create a mock right in your test that returns exactly what you want. Hopefully this answer serves as a decent intro to mocking. I would definitely suggest reading up on mocking frameworks to get a better feel for how the process works.
Use Rhino.Mocks library and call Expectations where ever appropriate. Following is a sample mocking your movie object.
using System;
using NUnit.Framework;
using Rhino.Mocks;
namespace ConsoleApplication1
{
public class Tomato
{
public Tomato(string t4qpkcsek5h6vgbsy8k4etxdd)
{
//
}
public virtual Movie FindMovieById(int i)
{
return null;
}
}
public class Movie
{
public string Title;
public Movie( )
{
}
public void FindMovieById(int i)
{
throw new NotImplementedException();
}
}
[TestFixture]
public class IndividualMovieTests
{
[Test]
public void Movie_Information_Is_Loaded_Correctly()
{
//Create Mock.
Tomato tomato = MockRepository.GenerateStub<Tomato>("t4qpkcsek5h6vgbsy8k4etxdd");
//Put expectations.
tomato.Expect(t=>t.FindMovieById(0)).IgnoreArguments().Return(new Movie(){Title ="Gone With The Wind"});
//Test logic.
Movie movie = tomato.FindMovieById(9818);
//Do Assertions.
Assert.AreEqual("Gone With The Wind", movie.Title);
//Verify expectations.
tomato.VerifyAllExpectations();
}
}
}