• No se han encontrado resultados

TALLER DE PADRES

In document TRABAJO DE FIN DE GRADO (página 76-0)

The Project @Test methods in the class ProjectTest demonstrate the level of testing

that takes advantage of the TracksApi abstractions.

These @Test methods do not use RestAssured and HttpMessageSender directly

because they are exploring entity level API messages, rather than HTTP format and endpoint access.

The ProjectTest class only covers three basic scenarios, but this was enough to

flush out TracksApi abstraction to cover GET, POST, PUT and DELETE scenarios.

Create Project

All of the REST API interaction in this test is carried out through the TracksApi

abstraction. Points to note:

The simple random data generator RandomDataGenerator is used to create

unique test data into the environment.

Assertions use the JUnit assertions rather than REST Assured.

RestAssured is used, but only because of a bleed over of the Response class.

I named the instantiated TracksApi variable as api. This results in the test being

easy to read and understand because it is not cluttered by the actual implementation details of the API calls.

@Test

public void aUserCanCreateAProject(){

TracksApi api = new TracksApi(TestEnvDefaults.getTestEnv()); // get the current set of projects

int totalProjectsForUser = api.getProjects().size(); // create a new project

String newProjectName = "A New Project" +

new RandomDataGenerator().randomWord(); Response response = api.createProject(newProjectName);

Assert.assertEquals(201, response.getStatusCode());

// get projects again and check the new project is in the list

List<TracksProject> theProjects = api.getProjects(); int newTotalProjectsForUser = theProjects.size(); Assert.assertTrue(newTotalProjectsForUser >

totalProjectsForUser); Boolean foundProject = false;

for(TracksProject project : theProjects){

if(project.getName().contentEquals(newProjectName)){ foundProject = true;

} }

Assert.assertTrue("Could not find project named " + newProjectName, foundProject); }

A few words about the assertions.

After creating a Project through the API and asserting on the response status code.

Response response = api.createProject(newProjectName); Assert.assertEquals(201, response.getStatusCode());

Check that the size of the Project list has increased.

List<TracksProject> theProjects = api.getProjects(); int newTotalProjectsForUser = theProjects.size(); Assert.assertTrue(newTotalProjectsForUser >

totalProjectsForUser);

Also check that the Project created was actually returned in the list of Projects.

Boolean foundProject = false;

for(TracksProject project : theProjects){

if(project.getName().contentEquals(newProjectName)){ foundProject = true;

} }

Assert.assertTrue("Could not find project named " + newProjectName, foundProject);

This might be viewed as overkill, but it avoids false positives where the total has increased, but the created value is not accessible.

The size check is performed first, because if the list hasn’t increased in size then we wouldn’t expect to find the Project, and there is no point spending time working through the list.

Amend

The ‘amend’ test creates a Project, and even repeats some of the assertions from the ‘Create’ test. i.e. checking for a 201 status code on creation.

Some people would view this as duplication and would not have the assertion for status code in this test. But I have retained it because if something goes wrong in the Amend assertions I want to make sure that the Project was actually created before I try to amend it.

I don’t want to re-use data from the Create test, and nor do I want to make tests dependent upon each other.

Also the abstraction layer means that repeating these assertions does not take up much code and doesn’t add to the maintenance overhead of the test.

This is the test that uses the ‘time based wait” synchronisation.

@Test

public void aUserCanAmendAProjectName(){

TracksApi api = new TracksApi(TestEnvDefaults.getTestEnv()); // create a new project

String newProjectName =

"A New Project" +

new RandomDataGenerator().randomWord(); api.createProject(newProjectName);

Assert.assertEquals(201, api.getLastResponse().

getStatusCode()); String projectId = new TracksResponseProcessor(

api.getLastResponse()) .getIdFromLocation(); TracksProject createdProject = api.getProject(projectId); Assert.assertEquals(newProjectName, createdProject.getName()); Wait.aFewSeconds(2);

// so that when we compare update times they are different

// amend the project

Map<String,String> fieldsToAmend =

new HashMap<String,String>(); fieldsToAmend.put("name", "the new name " +

new RandomDataGenerator().randomWord()); api.amendProject(projectId, fieldsToAmend );

TracksProject amendedProject = api.getProject(projectId); // check amended date has changed

contentEquals(

createdProject.getUpdatedAt())); // check created is the same

Assert.assertTrue(amendedProject.getCreatedAt(). contentEquals(

createdProject.getCreatedAt())); // check name changed

Assert.assertEquals(fieldsToAmend.get("name"), amendedProject.getName()); }

Delete

Given the extremes that I went to in the Create test to make sure that the Project was in the list after being created. It seems that I was a little more lax with the Delete test since I only check that the system reports the Project as missing with a 404 rather

than getting the full list of Projects and checking through the list.

@Test

public void aUserCanDeleteAProject(){

TracksApi api = new TracksApi(TestEnvDefaults.getTestEnv()); api.createProject("A New Project" +

new RandomDataGenerator().randomWord()); Assert.assertEquals(201,

api.getLastResponse().getStatusCode()); String projectId = new TracksResponseProcessor(

api.getLastResponse()) .getIdFromLocation(); // check we can get it

api.getProject(projectId); Assert.assertEquals(200,

api.getLastResponse().getStatusCode()); // check we can delete it

api.deleteProject(projectId); Assert.assertEquals(200,

api.getLastResponse().getStatusCode()); // check it has been deleted

api.getProject(projectId); Assert.assertEquals(404,

api.getLastResponse().getStatusCode()); }

I suspect that if I encountered a bug in the system with deletes then I might expand the test to GET all the Projects and iterate through the list.

I would do this by extracting the foundProject loop in the Create test (via an extract

this test.

At the moment this test only covers the basic conditions.

Summary

These tests provide a useful opportunity for discussing how much we should assert, and how much we should test.

In theory, I could have combined the Create and the Amend test, since I have to create a Project before I can amend it. I could even have created, amended and then deleted in the same test and had a single test in this class.

One of the many guidelines around creating @Test methods is that “the @Test should

only have one assert”. Clearly I don’t do that. I think it is appropriate to assert on whatever you need to.

My guiding rule was more like “the @Test should only have one intent”. So I have a

‘Create’ test, an ‘Amend’ test and a ‘Delete’ test. I don’t control the order that tests run in, so in theory, if a ‘create’ failed then all the tests would fail. But because I have a ‘Create’ test, I know that if it fails, then I would investigate that failure first. The ‘Create’ and ‘Amend’ both have fairly weak coverage. After identifying that a ‘Create’ works, I would normally have some data driven tests for covering more of the create conditions e.g. different lengths of name, different characters in names, can’t create with no name, etc.

Similarly the ‘Amend’ test is crying out for more coverage.

But, these tests are to demonstrate mechanisms, rather than serve as good examples of effective condition coverage.

In document TRABAJO DE FIN DE GRADO (página 76-0)

Documento similar