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.
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 ...
}
I'm writing integration tests for my .NET Core app using xUnit. I have various things I want to test, but for brevity I'll use this example. I need to test these things ("document" is just a string, I'm not dealing with files):
Register new employee
Log in as employee
Add a new document for the employee
Add a comment to the document
Tests are of course supposed to run independently of each other, but I cannot add a comment to a nonexistent document. I need the document to exist to test adding a comment, but the employee must also exist to add the document.
I have test classes written for all entities I want to use, but I don't know how to actually get them in the database for a given test. How can I "seed" the DB for a given test?
TestStartup.cs:
public class TestFixture : IDisposable
{
readonly TestServer _server;
public HttpClient _client { get; }
public TestFixture()
{
var builder = new WebHostBuilder()
.UseContentRoot(#"..\..\..\..\..\src\MyProj.Web")
.UseEnvironment("IntegrationTestDevelopment")
.UseStartup<Web.Startup>();
_server = new TestServer(builder);
_client = _server.CreateClient();
_client.BaseAddress = new Uri("http://localhost:2856");
}
public void Dispose()
{
_client.Dispose();
_server.Dispose();
}
}
Relevant part of Web.Startup:
services.AddEntityFrameworkInMemoryDatabase()
.AddDbContext<MyContext>(options => options.UseInMemoryDatabase());
Example test class:
public class EmployeeTests : IClassFixture<TestFixture>
{
public HttpClient _client;
public EmployeeTests(TextFixture fixture)
{
_client = fixture._client;
}
[Theory]
[MemberData("ValidRegistrations")]
public async Task Register_new_employee_succeeds(EmployeeModel model)
{
var request = Helpers.CreateRequestMessage("POST", "/api/employees", model);
var response = await _client.SendAsync(request);
Assert.Equal(HttpStatusCode.Created, response.StatusCode);
}
}
I may be missing something here (knowledge of, but never used, .Net Core), but as you say this is an integration test rather than a pure unit test, so would an alternative approach be:
Have the integration test merely test the call / connectivity to the service (i.e to test that 'employees' can be called I think in your scenario - regardless of database content)
Create a new test (ideally a unit test, but let's face it integration tests definately have a place in this world) to test the functionality of the 'employees' method / function
If the above isn't an option for you, how about considering seeding the db in the test class (one of the method or class set up methods - I forget their names in xUnit) by calling simple database interaction code, doesn't have to be anything exotic or fancy if it's just a seed followed by a subsequent end-of-test tear down. A basic db connection and query execution would presumably suffice.
Appreciate this doesn't directly answer your question, more of an outsiders viewpoint.
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.
I use PayPal's service on Service References. As is known to have a PayPal SandBox environment.
I know a site is running on local or not.
My problem is that I want to dynamically change the namespace of the class by the environment (because the two classes have the same function). Eventually I want I do not have to change any code, but once automatically when the site is run by local the sandbox will and when he runs on the server is the real product will.
I want to switch between this:
using DynoPayPal.PayPal;
And this:
using DynoPayPal.SandBoxPayPal;
So that the object: PaymentDetailsItemType for example, will "be" DynoPayPal.PayPal in the first case, and DynoPayPal.SandBoxPayPal in the second case.
You cannot change the namespace of a class. That is not what they are there for, and it makes no sense whatsoever to do so. But, as the comments to your question point out, what you are looking for is dependency injection. Consider this:
public interface IPayPal
{
void aMethod();
}
public class PayPal : IPayPal { /* ... */ }
public class SandboxPayPal : IPayPal { /* ... */ }
Then we can perhaps envisage a class that will give us the right one based on the environment:
public sealed class PayPalFactory
{
public IPayPal Create()
{
if(EnvironmentIsLive) // replace this with a proper check!
return new PayPal();
else
return new SandboxPayPal();
}
}
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();
}
}
}