.netcore continuous integration method of test article of the reform

Series catalog

By the previous two sections explain our test class already has two test methods, and generally follows

 public class mvc20
    {
        private readonly HttpClient _client;

        public mvc20()
        {
            var builder = new WebHostBuilder()
                .UseContentRoot(@"E:\personal project\newTest2018\ConsoleApp1\CoreMvc")
                .UseEnvironment("Development")
                .UseStartup<CoreMvc.Startup>();
            var server = new TestServer(builder);
             _client = server.CreateClient();
        }
        [Fact]
        public async Task SimpleGet()
        {
            var response = await _client.GetAsync("/HelloWorld/Hello");
            response.EnsureSuccessStatusCode();
            var responseStr = await response.Content.ReadAsStringAsync();
            Assert.Equal("Hello,World", responseStr);
        }

        [Theory]
        [AutoData]
        public async Task SimplePost(Student stud)
        {
            var content = new StringContent(JsonConvert.SerializeObject(stud), Encoding.UTF8, "application/json");
            var response = await _client.PostAsync("/HelloWorld/StudentInfo", content);
            response.EnsureSuccessStatusCode();
            var result = await response.Content.ReadAsStringAsync();
            Assert.True(!string.IsNullOrEmpty(result));
        }
    }

Improved one: move the outer class object initialization

The above method seemed to have no problem, in fact, there is a performance trap, we already know that through knowledge of the previous section, the constructor xunit in the test class will perform again at the time of each test method runs, we usually test code is far more than three several, sometimes dozens or even hundreds. in this way each time to create a very affecting performance. TestServer and _client here and are not released. there is also a web project in every possible test class all you need to create such a TestServer, such duplicate code is copied many times, bringing difficult to maintain.

We've talked about before, if we want to make a solid object is initialized only once in a test class, we should let this class implements IClassFixture generic interfaces, classes will be automatically injected into the generic object initialization time, and only initialization time, if the generic object implements the IDisposable interface, this object will be executed inside the Dispose method when testing class for all execution methods are completed.

First we create a named MyTestServerFixtrueclass initialization TestServer HttpClient object and executed here. Code as follows

public class MyTestServerFixtrue:IDisposable
    {
        public readonly HttpClient _client;
        private readonly TestServer _server;
        public MyTestServerFixtrue()
        {
            var builder = new WebHostBuilder()
                .UseContentRoot(@"E:\personal project\newTest2018\ConsoleApp1\CoreMvc")
                .UseEnvironment("Development")
                .UseStartup<CoreMvc.Startup>();
             _server = new TestServer(builder);
            _client = _server.CreateClient();
        }

        public void Dispose()
        {
            _client.Dispose();
            _server.Dispose();
        }

Here most of the methods and parameters added in the previous test in the same class, but the following points should be noted:
1. The server variables in the constructor outside, so that we can put it in the Dispose freed, or not to locate it.
2. type the client to become public, because we need to access it in the test class.

Let's look at the code after the test class transformation

 public class mvc20:IClassFixture<MyTestServerFixtrue>
    {
        private readonly HttpClient _client;

        public mvc20(MyTestServerFixtrue fixtrue)
        {
            this._client = fixtrue._client;
        }
    }

Here is the main code, the first to achieve a IClassFixture, then we change the no-argument constructor to have the Senate, and passed MyTestServerFixtrue type of object, Xunit will automatically inject the object, and then we put this object assigned to this class of httpclient the _client objects, so that we can use it in this class.

Other tests like this can also be achieved IClassFixture<MyTestServerFixtrue>, if you want to change the configuration TestServer only need to change on the line in MyTestServerFixtrue class.

Two improvements: fixed route parameters

We see the path in front of the two test methods mentioned submissions contain "/ HelloWorld", it is actually matches the name of the controller, the same below a general test methods of the Controller under test are written in the same class. such Controller name is fixed, we can make it alone pulled out, only routing behind the Action.

We put the test into the following categories:

 public class mvc20:IClassFixture<MyTestServerFixtrue>
    {
        private readonly HttpClient _client;

        public mvc20(MyTestServerFixtrue fixtrue)
        {
            var baseAddr = fixtrue._client.BaseAddress.AbsoluteUri;
            string controllerName ="HelloWorld";
            this._client = fixtrue._client;
            if (!fixtrue._client.BaseAddress.AbsoluteUri.Contains(controllerName))
            {
                fixtrue._client.BaseAddress = new Uri(baseAddr + controllerName+"/");
            }
        }
        [Fact]
        public async Task SimpleGet()
        {
            var response = await _client.GetAsync($"{nameof(HelloWorldController.Hello)}");
            response.EnsureSuccessStatusCode();
            var responseStr = await response.Content.ReadAsStringAsync();
            Assert.Equal("Hello,World", responseStr);
        }

        [Theory]
        [AutoData]
        public async Task SimplePost(Student stud)
        {
            var content = new StringContent(JsonConvert.SerializeObject(stud), Encoding.UTF8, "application/json");
            var response = await _client.PostAsync($"{nameof(HelloWorldController.StudentInfo)}", content);
            response.EnsureSuccessStatusCode();
            var result = await response.Content.ReadAsStringAsync();
            Assert.True(!string.IsNullOrEmpty(result));
        }
    }

Here we have the name of the controller is added to the HttpClient BaseUrl inside, then send get, post, etc. Action requested whenever the name of the benefits we use nameof keywords to get action name, use nameof keywords to get here is: first First, we click on the name of the method can quickly locate a method to specify. more importantly, if the method name changed, compile compile time error occurs, we can quickly locate the error and then modified.

Improved three: resource path to a relative path

Above MyTestServerFixtrue class code has an obvious problem: that is UseContentRoot where the path is hard-coded, the project on the local address and the vast majority of cases under or with other colleagues on the server is not the same (because we directory name where the project is not the same as) this time if other people call these code errors may occur.

We can use a relative path to get the absolute way to solve this problem, because the main file folder both projects in the same folder, so the test project back several layers can be obtained outside the home directory of the mvc project.

We will be MyTestServerFixtruethe class constructor to read as follows:

public MyTestServerFixtrue()
        {
            var rootPath = GetContentRootDir();
            var builder = new WebHostBuilder()
                .UseContentRoot(rootPath)
                .UseEnvironment("Development")
                .UseStartup<CoreMvc.Startup>();
             _server = new TestServer(builder);
            _client = _server.CreateClient();
        }

This time we do not write rootPath dead but by the method GetContentRootDirto get.
Here we look at this GetContentRootDirmethod

 private string GetContentRootDir()
        {
            var currentPath = AppDomain.CurrentDomain.BaseDirectory;
            var relativePath = @"..\..\..\..\CoreMvc";
            var combinedPath = Path.Combine(currentPath, relativePath);
            var absPath = Path.GetFullPath(combinedPath);
            return absPath;
        }

First, let's get the domain directory of the current program, which is running the program directory, access to it after we look at how many layers move up to reach the file contains mvc project and the test project folder, and upon inspection is four, following relatively we wrote to the path as variable relativePathas defined.
we put them together, and then to get the absolute path to a relative path by Path.GetFullPath.

Improved four set timeout

Sometimes a server failure will lead to very slow request, the server can not return requests for a long time, which can lead to integrated test code has been 'card' with can not be completed at this time you can set a time-out. Setup is easy, HttpClient has a Timeout property, set the appropriate timeout to .HttpClient default request timeout is 100s, this value should not need to change most of the time, but on specific business, there may be some method itself particularly long execution time (business logic is very complicated, sql statement is very complex, etc.) at this time to this request unit may set a timeout. for example, is 150s, as set

     CancellationTokenSource cts = new CancellationTokenSource(TimeSpan.FromSeconds(150));
            var response = await client.GetAsync("/Home/index", cts.Token);

Defined herein a CancellationTokenSource object and specify the timeout, then this object Token pass asynchronous request method.

Guess you like

Origin www.cnblogs.com/tylerzhou/p/11355755.html