-
Notifications
You must be signed in to change notification settings - Fork 260
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improvement: ClassCleanup method to trigger after worker has executed all test methods not after all workers are complete #580
Comments
Assembly and class cleanup today run at the end. Could you please elaborate on why this would block your development ? |
@KitoCoding any updates here? |
once a class has run all of its test cases, there is no reason to not run the test class clean up method right away. the test class is complete, so run the method... it may just be the way I imagining test cases, but it will make parallel tests much easier. |
For example, lets say i have 50 test classes, and since msTest doesn't run test classes or methods in order, I have my first test method run and its the only method in the class. I have to wait for 50 test classes to complete running before I am able to run the test class clean up method. Thats just silly |
@KitoCoding Are you dependent on the cleanup done in one test class in the cleanup/initialize of another test class ? What does your cleanup do ? |
@cltshivash In my use case we're running acceptance tests and our ClassCleanup is cleaning up test mocks. Cleaning them up earlier would reduce our chance of mock collisions and make debugging test issues easier (less concurrent mocks). |
Hi @cltshivash, |
@vlaskal @cfletcher Thanks for the details. It helps provide the context. If one of you can contribute this change that would be great. |
@cltshivash I can try but also some advice would be good. I already look into the code and found a trouble with DoNoTParallelize attribute. I think even this test should be done between same ClassInitialize and ClassCleanup and maybe also on same thread (right now it is on different thread). Right now it is executed after all parallel tests in project. |
In my case this a blocking point for using MSTest. Our test classes create an HTTPListener for a specific port. Since
This mean, that for every test class that we add, we need to use a new port so every developer needs to run again the In other project we have a similar issue because we use a third-party that it's binding in UDP/TCP ports, if it cannot bind in a port it tries within a range, but that range is no longer large enough because of the number of test classes we have. The workaround is to move the initialize and clean-up code per class to per test, making the tests slower. I think that any project using networking will find this as a real issue, because we cannot release the resources between test classes. |
I would just like to know the reasons as to why class clean up is triggered after all workers are complete. i understand if you are running parralelle runs at a method level. Sure, with no way for MSTest v2 to order the test cases, then it makes sense to just run after all workers are complete. But you supply the option to run at a test class level. One worker per test class. This is what we use, A worker should run the test init, all its methods then its clean up. |
Implementing with DoNoTParallelize attribute might be tricky, if thats the case. A warning should go out to the user. "TestCleanUp cannot be triggered until all Workers are complete due to the DoNoTParallelize attribute found [insert location]." If a worker is scheduled to run at a test class level and all its methods are good to go (does not have the DoNoTParallelize attribute) then trigger the class clean up. If a worker encounters a DoNoTParallelize attribute, then that worker cannot trigger test clean up until the very end. But my methods assume workers understand the order in which they are going to work, and they also are aware of other workers and their jobs. Essentially: If a worker encounters a TestMethod with the DoNoTParallelize attribute, then do not trigger its TestClassClean up method until all workers are complete. Make sure to log warning so the user knows why its clean ups are not triggering |
What?! First, why is this not documented and second, I don't fully understand the decision to implement it this way. |
It is not documented because it is a bug. They have admitted that this isn't as designed but its a low priority. |
Well, if there is a bug that completely changes the test life cycle I would expect it to be well documented. |
I already look at source code and found that it is by code design. Of course it is a bug. But code is written this way.
It is simple and straight forward design. And I think there will be lots of issues when you try to change it into proper flow. |
I would like to know what is expected behavior in following scenario.
|
I have also found that the test class constructor is invoked before each test. |
It looks like the thread has already made some valid use cases, but here's another example: Using Appium.WebDriver to run integration tests on a single-instance application with non-trivial load and close. I would expect that each TestClass could be used to launch the application into a specific state, run the unit tests on that state, then close the application. Because the ClassCleanup doesn't run before the next class attempts to start testing, the driver attempts to launch the application a second time in a different mode while the first instance is still open. We're currently using TestInitialize and TestCleanup to allow a "Run All" to succeed, but this significantly limits our ability to break apart our integration tests into separate, well defined TestMethods due to how much execution time it adds. Of course, initialization and clean-up at arbitrary levels of granularity (ex: groups) could help minimize execution time further, by batching tests to minimize state changes within an application run, but TestClasses would cover most of that need (by cutting out the load and shutdown cycle). |
My team is also experiencing an issue with this design of when ClassCleanup is called. I figured I would contribute our use case for this thread. For us, we are developing database integration tests where each test class tests a partition of our database access code. We could force all of these tests to run sequentially, resetting the database after each test run, but this is extremely expensive. To allow for more parallelism and avoid having to reset so often, we want to create a test database for each one of these test classes. Creating one database per test class (thus per partition of our entire app) is too much. This lead us to a solution where we create a fixed set of databases these classes can use, reset after they are done, and release the database connection back to the pool for other test classes to use. With ClassCleanup happening after all other tests classes run, these test classes will never have the chance to release their resources for other test classes to use. As a result, the lack of this feature is keeping us from using this library even though we really love it and is familiar with everything else we do. Hope this helps. |
Been open for a while, any update? |
any updates? |
this would be very useful for me as well |
This is very helpful while working with database tests. Each ClassInitialize creates necessary tables, storedprocedures, feeding data and other necessary objects in sql server database and it should have provision to drop them withing the same classes's ClassCleanup. If we are not able to do so, another test class might be using the same object and it is failed. |
This is fixed in #968 and released with 2.2.8. To enable it you can set ClassCleanupBehavior to ClassCleanupBehavior.EndOfClass in your ClassCleanup attribute. This can also be set on assembly level by ClassCleanupExecution assembly level attribute. |
Thank you! |
Seems it's not working for me, anyone tried it? |
Didn't work for me as well |
Tried it again. Works fine for me. Did you specify the correct behavior to the attribute?
<!-- file mstest35.csproj -->
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
<PackageReference Include="MSTest.TestAdapter" Version="2.2.8" />
<PackageReference Include="MSTest.TestFramework" Version="2.2.8" />
<PackageReference Include="coverlet.collector" Version="3.1.0" />
</ItemGroup>
</Project>
// file UnitTest1.cs
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.IO;
using System.Threading;
[assembly: ClassCleanupExecution(ClassCleanupBehavior.EndOfClass)]
namespace mstest35;
public static class LogHelper
{
public static void AppendLine(string text) => File.AppendAllText(
Path.Combine(AppContext.BaseDirectory, "log.txt"),
text + "\n");
}
[TestClass]
public class UnitTest1
{
[ClassCleanup]
public static void Cleanup()
{
LogHelper.AppendLine($"{nameof(UnitTest1)} done.");
}
[TestMethod]
public void TestMethod1()
{
LogHelper.AppendLine($"{nameof(UnitTest1)} start.");
}
}
[TestClass]
public class UnitTest2
{
[ClassCleanup]
public static void Cleanup()
{
LogHelper.AppendLine($"{nameof(UnitTest2)} done.");
}
[TestMethod]
public void TestMethod1()
{
LogHelper.AppendLine($"{nameof(UnitTest2)} start.");
Thread.Sleep(1000);
}
} |
When running multiple [TestClass]es at the same time, why does the [ClassCleanup()] methods only get called at the very end?
File: UnitTest1.cs
File: UnitTest2.cs
The execution order is the following:
Notice that both [ClassCleanup] methods are executed at the end of the set; not after each TestClass is "done".
But I expected a different behavior:
Microsoft's Documentation - ClassCleanupAttribute Class says, "Identifies a method that contains code to be used after ALL the tests in the test class have run and to free resources obtained by the test class."
But it appears to be run/executed late, after ALL tests have run. This seems wrong, and it is blocking my development.
The text was updated successfully, but these errors were encountered: